Split duplicated code out to LocalTexture

This commit is contained in:
Sollace 2018-07-25 16:29:35 +02:00
parent 865535d49e
commit 51362be542
7 changed files with 202 additions and 178 deletions

View file

@ -0,0 +1,118 @@
package com.voxelmodpack.hdskins;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.SkinManager.SkinAvailableCallback;
import net.minecraft.util.ResourceLocation;
public class LocalTexture {
private final TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
private DynamicTexture local;
private PreviewTexture remote;
private ResourceLocation remoteResource;
private ResourceLocation localResource;
private final IBlankSkinSupplier blank;
private final Type type;
public LocalTexture(GameProfile profile, Type type, IBlankSkinSupplier blank) {
this.blank = blank;
this.type = type;
String file = type.name().toLowerCase() + "s/preview_${profile.getName()}.png";
remoteResource = new ResourceLocation(file);
textureManager.deleteTexture(remoteResource);
reset();
}
public ResourceLocation getTexture() {
if (hasRemote()) {
return remoteResource;
}
return localResource;
}
public void reset() {
localResource = blank.getBlankSkin(type);
}
public boolean hasRemote() {
return remote != null;
}
public boolean hasLocal() {
return local != null;
}
public boolean usingLocal() {
return !hasRemote() && hasLocal();
}
public boolean uploadComplete() {
return hasRemote() && remote.isTextureUploaded();
}
public PreviewTexture getRemote() {
return remote;
}
public void setRemote(PreviewTextureManager ptm, SkinAvailableCallback callback) {
clearRemote();
remote = ptm.getPreviewTexture(remoteResource, type, blank.getBlankSkin(type), callback);
}
public void setLocal(File file) {
if (!file.exists()) {
return;
}
clearLocal();
try {
BufferedImage image = ImageIO.read(file);
BufferedImage bufferedImage = new ImageBufferDownloadHD().parseUserSkin(image);
local = new DynamicTextureImage(bufferedImage);
localResource = textureManager.getDynamicTextureLocation("localSkinPreview", local);
} catch (IOException e) {
e.printStackTrace();
}
}
private void clearRemote() {
if (hasRemote()) {
remote = null;
textureManager.deleteTexture(remoteResource);
}
}
public void clearLocal() {
if (hasLocal()) {
local = null;
textureManager.deleteTexture(localResource);
localResource = blank.getBlankSkin(type);
}
}
public interface IBlankSkinSupplier {
ResourceLocation getBlankSkin(Type type);
}
}

View file

@ -4,36 +4,28 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.voxelmodpack.hdskins.DynamicTextureImage;
import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.ImageBufferDownloadHD;
import com.voxelmodpack.hdskins.PreviewTexture;
import com.voxelmodpack.hdskins.LocalTexture;
import com.voxelmodpack.hdskins.LocalTexture.IBlankSkinSupplier;
import com.voxelmodpack.hdskins.PreviewTextureManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.client.resources.SkinManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.ResourceLocation;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import javax.imageio.ImageIO;
@SuppressWarnings("EntityConstructor")
public class EntityPlayerModel extends EntityLivingBase {
public class EntityPlayerModel extends EntityLivingBase implements IBlankSkinSupplier {
public static final ResourceLocation NO_SKIN = new ResourceLocation("hdskins", "textures/mob/noskin.png");
public static final ResourceLocation NO_ELYTRA = new ResourceLocation("textures/entity/elytra.png");
private Map<EntityEquipmentSlot, ItemStack> armour = Maps.newEnumMap(ImmutableMap.of(
private final Map<EntityEquipmentSlot, ItemStack> armour = Maps.newEnumMap(ImmutableMap.of(
EntityEquipmentSlot.HEAD, ItemStack.EMPTY,
EntityEquipmentSlot.CHEST, ItemStack.EMPTY,
EntityEquipmentSlot.LEGS, ItemStack.EMPTY,
@ -41,133 +33,56 @@ public class EntityPlayerModel extends EntityLivingBase {
EntityEquipmentSlot.MAINHAND, ItemStack.EMPTY
));
private PreviewTexture remoteSkinTexture;
private ResourceLocation remoteSkinResource;
protected ResourceLocation localSkinResource;
private DynamicTexture localSkinTexture;
private PreviewTexture remoteElytraTexture;
private ResourceLocation remoteElytraResource;
private ResourceLocation localElytraResource;
private DynamicTexture localElytraTexture;
private TextureManager textureManager;
protected final LocalTexture skin;
protected final LocalTexture elytra;
public final GameProfile profile;
protected boolean remoteSkin = false;
protected boolean hasLocalTexture = false;
protected boolean previewThinArms = false;
public EntityPlayerModel(GameProfile gameprofile) {
super(new DummyWorld());
profile = gameprofile;
textureManager = Minecraft.getMinecraft().getTextureManager();
remoteSkinResource = new ResourceLocation("skins/preview_" + profile.getName() + ".png");
remoteElytraResource = new ResourceLocation("elytras/preview_" + profile.getName() + ".png");
localSkinResource = getBlankSkin();
localElytraResource = getBlankElytra();
textureManager.deleteTexture(remoteSkinResource);
textureManager.deleteTexture(remoteElytraResource);
skin = new LocalTexture(profile, Type.SKIN, this);
elytra = new LocalTexture(profile, Type.ELYTRA, this);
}
public void reloadRemoteSkin(SkinManager.SkinAvailableCallback listener) {
remoteSkin = true;
if (remoteSkinTexture != null) {
textureManager.deleteTexture(remoteSkinResource);
}
if (remoteElytraTexture != null) {
textureManager.deleteTexture(remoteElytraResource);
}
PreviewTextureManager ptm = HDSkinManager.getPreviewTextureManager(profile);
remoteSkinTexture = ptm.getPreviewTexture(remoteSkinResource, Type.SKIN, getBlankSkin(), listener);
remoteElytraTexture = ptm.getPreviewTexture(remoteElytraResource, Type.ELYTRA, getBlankElytra(), null);
skin.setRemote(ptm, listener);
elytra.setRemote(ptm, listener);
}
public void setLocalTexture(File skinTextureFile, Type type) {
if (skinTextureFile.exists()) {
if (type == Type.SKIN) {
remoteSkin = false;
if (localSkinTexture != null) {
textureManager.deleteTexture(localSkinResource);
localSkinTexture = null;
}
BufferedImage bufferedImage;
try {
BufferedImage image = ImageIO.read(skinTextureFile);
bufferedImage = new ImageBufferDownloadHD().parseUserSkin(image);
assert bufferedImage != null;
} catch (IOException var4) {
localSkinResource = getBlankSkin();
var4.printStackTrace();
return;
}
localSkinTexture = new DynamicTextureImage(bufferedImage);
localSkinResource = textureManager.getDynamicTextureLocation("localSkinPreview", localSkinTexture);
hasLocalTexture = true;
skin.setLocal(skinTextureFile);
} else if (type == Type.ELYTRA) {
remoteSkin = false;
if (localElytraTexture != null) {
textureManager.deleteTexture(localElytraResource);
localElytraTexture = null;
}
BufferedImage bufferedImage;
try {
bufferedImage = ImageIO.read(skinTextureFile);
} catch (IOException var4) {
localElytraResource = getBlankElytra();
var4.printStackTrace();
return;
}
localElytraTexture = new DynamicTextureImage(bufferedImage);
localElytraResource = textureManager.getDynamicTextureLocation("localElytraPreview", localElytraTexture);
hasLocalTexture = true;
}
skin.setLocal(skinTextureFile);
}
}
protected ResourceLocation getBlankSkin() {
return NO_SKIN;
}
protected ResourceLocation getBlankElytra() {
return NO_ELYTRA;
@Override
public ResourceLocation getBlankSkin(Type type) {
return type == Type.SKIN ? NO_SKIN : NO_ELYTRA;
}
public boolean isUsingLocalTexture() {
return !remoteSkin && hasLocalTexture;
return skin.usingLocal() || elytra.usingLocal();
}
public boolean isTextureSetupComplete() {
return (remoteSkin && remoteSkinTexture != null) && remoteSkinTexture.isTextureUploaded();
return skin.uploadComplete() && elytra.uploadComplete();
}
public void releaseTextures() {
if (localSkinTexture != null) {
textureManager.deleteTexture(localSkinResource);
localSkinTexture = null;
localSkinResource = getBlankSkin();
hasLocalTexture = false;
}
if (localElytraTexture != null) {
textureManager.deleteTexture(localElytraResource);
localElytraTexture = null;
localElytraResource = getBlankElytra();
hasLocalTexture = false;
}
skin.clearLocal();
elytra.clearLocal();
}
public ResourceLocation getSkinTexture() {
return remoteSkin ? (remoteSkinTexture != null ? remoteSkinResource
: DefaultPlayerSkin.getDefaultSkin(entityUniqueID)) : localSkinResource;
}
public ResourceLocation getElytraTexture() {
return remoteSkin && remoteElytraTexture != null ? remoteElytraResource : localElytraResource;
public LocalTexture getLocal(Type type) {
return type == Type.SKIN ? skin : elytra;
}
public void setPreviewThinArms(boolean thinArms) {
@ -175,24 +90,13 @@ public class EntityPlayerModel extends EntityLivingBase {
}
public boolean usesThinSkin() {
if (isTextureSetupComplete() && remoteSkinTexture.hasModel()) {
return remoteSkinTexture.usesThinArms();
if (skin.uploadComplete() && skin.getRemote().hasModel()) {
return skin.getRemote().usesThinArms();
}
return previewThinArms;
}
@Override
public void swingArm(EnumHand hand) {
super.swingArm(hand);
if (!isSwingInProgress || swingProgressInt >= 4 || swingProgressInt < 0) {
swingProgressInt = -1;
isSwingInProgress = true;
swingingHand = hand;
}
}
public void updateModel() {
prevSwingProgress = swingProgress;
if (isSwingInProgress) {
@ -205,7 +109,7 @@ public class EntityPlayerModel extends EntityLivingBase {
swingProgressInt = 0;
}
swingProgress = swingProgressInt / 8;
swingProgress = swingProgressInt / 8F;
}
@Override
@ -227,5 +131,4 @@ public class EntityPlayerModel extends EntityLivingBase {
public void setItemStackToSlot(EntityEquipmentSlot slotIn, ItemStack stack) {
armour.put(slotIn, stack);
}
}

View file

@ -9,6 +9,7 @@ import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
import com.voxelmodpack.hdskins.HDSkinManager;
import com.voxelmodpack.hdskins.skins.SkinServer;
@ -69,10 +70,12 @@ public class GuiSkins extends GuiScreen {
private String uploadError;
private volatile String skinMessage = I18n.format("hdskins.choose");
private String skinUploadMessage = I18n.format("hdskins.request");
private volatile boolean fetchingSkin;
private volatile boolean uploadingSkin;
private volatile boolean pendingRemoteSkinRefresh;
private volatile boolean throttledByMojang;
private int refreshCounter = -1;
private ThreadOpenFilePNG openFileThread;
private final Object skinLock = new Object();
@ -127,6 +130,7 @@ public class GuiSkins extends GuiScreen {
localPlayer.updateModel();
remotePlayer.updateModel();
if (fetchingSkin && remotePlayer.isTextureSetupComplete()) {
fetchingSkin = false;
btnClear.enabled = true;
@ -237,36 +241,35 @@ public class GuiSkins extends GuiScreen {
private void loadLocalFile(File skinFile) {
Minecraft.getMinecraft().addScheduledTask(localPlayer::releaseTextures);
if (!skinFile.exists()) {
skinMessage = I18n.format("hdskins.error.unreadable");
} else if (!FilenameUtils.isExtension(skinFile.getName(), new String[]{"png", "PNG"})) {
skinMessage = I18n.format("hdskins.error.ext");
} else {
BufferedImage chosenImage;
try {
chosenImage = ImageIO.read(skinFile);
} catch (IOException var6) {
skinMessage = I18n.format("hdskins.error.open");
var6.printStackTrace();
return;
}
BufferedImage chosenImage = ImageIO.read(skinFile);
if (chosenImage == null) {
skinMessage = I18n.format("hdskins.error.open");
} else if (isPowerOfTwo(chosenImage.getWidth())
&& (chosenImage.getWidth() == chosenImage.getHeight() * 2
|| chosenImage.getWidth() == chosenImage.getHeight())
&& chosenImage.getWidth() <= MAX_SKIN_DIMENSION
&& chosenImage.getHeight() <= MAX_SKIN_DIMENSION) {
} else if (!acceptsSkinDimensions(chosenImage.getWidth(), chosenImage.getHeight())) {
skinMessage = I18n.format("hdskins.error.invalid");
} else {
synchronized (skinLock) {
pendingSkinFile = skinFile;
}
} else {
skinMessage = I18n.format("hdskins.error.invalid");
}
} catch (IOException var6) {
skinMessage = I18n.format("hdskins.error.open");
var6.printStackTrace();
}
}
}
protected boolean acceptsSkinDimensions(int w, int h) {
return isPowerOfTwo(w) && w == h * 2 || w == h && w <= MAX_SKIN_DIMENSION && h <= MAX_SKIN_DIMENSION;
}
@Override
protected void actionPerformed(GuiButton guiButton) {
if (openFileThread == null && !uploadingSkin) {
@ -279,28 +282,21 @@ public class GuiSkins extends GuiScreen {
openFileThread = new ThreadOpenFilePNG(mc, I18n.format("hdskins.open.title"), this::onFileOpenDialogClosed);
openFileThread.start();
guiButton.enabled = false;
}
if (guiButton.id == btnUpload.id) {
} else if (guiButton.id == btnUpload.id) {
if (selectedSkin != null) {
punchServer("hdskins.upload", selectedSkin.toURI());
btnUpload.enabled = false;
} else {
setUploadError(I18n.format("hdskins.error.select"));
}
}
if (guiButton.id == btnClear.id && remotePlayer.isTextureSetupComplete()) {
} else if (guiButton.id == btnClear.id && remotePlayer.isTextureSetupComplete()) {
punchServer("hdskins.request", null);
btnUpload.enabled = selectedSkin != null;
}
if (guiButton.id == btnBack.id) {
} else if (guiButton.id == btnBack.id) {
mc.displayGuiScreen(new GuiMainMenu());
}
if (guiButton.id == btnModeSkin.id || guiButton.id == btnModeElytra.id || guiButton.id == btnModeSkinnySkin.id) {
} else if (guiButton.id == btnModeSkin.id || guiButton.id == btnModeElytra.id || guiButton.id == btnModeSkinnySkin.id) {
ItemStack stack;
if (guiButton.id == btnModeSkin.id) {
thinArmType = false;
textureType = SKIN;
@ -402,11 +398,11 @@ public class GuiSkins extends GuiScreen {
float scale = height / 4;
float lookX = mid - mouseX;
mc.getTextureManager().bindTexture(localPlayer.getSkinTexture());
mc.getTextureManager().bindTexture(localPlayer.getLocal(Type.SKIN).getTexture());
renderPlayerModel(localPlayer, xPos1, yPos, scale, horizon - mouseY, lookX, partialTick);
mc.getTextureManager().bindTexture(remotePlayer.getSkinTexture());
mc.getTextureManager().bindTexture(remotePlayer.getLocal(Type.SKIN).getTexture());
renderPlayerModel(remotePlayer, xPos2, yPos, scale, horizon - mouseY, lookX, partialTick);
@ -421,15 +417,16 @@ public class GuiSkins extends GuiScreen {
enableBlend();
depthMask(false);
int labelwidth = (width / 2 - 80) / 2;
if (!localPlayer.isUsingLocalTexture()) {
int opacity = fontRenderer.getStringWidth(skinMessage) / 2;
Gui.drawRect(40, height / 2 - 12, width / 2 - 40, height / 2 + 12, 0xB0000000);
fontRenderer.drawStringWithShadow(skinMessage, (int) (xPos1 - opacity), height / 2 - 4, 0xffffff);
}
if (btnModeSkin.isMouseOver() || btnModeElytra.isMouseOver() || btnModeSkinnySkin.isMouseOver()) {
int y = Math.max(mouseY, 16);
String text;
if (btnModeSkin.isMouseOver()) {
text = "hdskins.mode.skin";
} else if (btnModeSkinnySkin.isMouseOver()) {
@ -437,6 +434,7 @@ public class GuiSkins extends GuiScreen {
} else {
text = "hdskins.mode.elytra";
}
drawHoveringText(I18n.format(text), mouseX, y);
}
if (btnAbout.isMouseOver()) {
@ -448,7 +446,7 @@ public class GuiSkins extends GuiScreen {
int lineHeight = throttledByMojang ? 16 : 12;
Gui.drawRect((int) (xPos2 - labelwidth), height / 2 - lineHeight, width - 40, height / 2 + lineHeight, 0xB0000000);
Gui.drawRect((int) (xPos2 - width / 4 + 40), height / 2 - lineHeight, width - 40, height / 2 + lineHeight, 0xB0000000);
if (throttledByMojang) {
drawCenteredString(fontRenderer, I18n.format("hdskins.error.mojang"), (int)xPos2, height / 2 - 10, 0xffffff);
@ -460,9 +458,9 @@ public class GuiSkins extends GuiScreen {
if (uploadingSkin || uploadOpacity > 0) {
if (!uploadingSkin) {
uploadOpacity -= deltaTime * 0.05F;
uploadOpacity -= deltaTime / 20;
} else if (uploadOpacity < 1) {
uploadOpacity += deltaTime * 0.1F;
uploadOpacity += deltaTime / 10;
}
if (uploadOpacity > 1) {
@ -488,8 +486,7 @@ public class GuiSkins extends GuiScreen {
enableDepth();
}
private void renderPlayerModel(EntityPlayerModel thePlayer, float xPosition, float yPosition, float scale, float mouseY, float mouseX,
float partialTick) {
private void renderPlayerModel(EntityPlayerModel thePlayer, float xPosition, float yPosition, float scale, float mouseY, float mouseX, float partialTick) {
enableColorMaterial();
pushMatrix();
translate(xPosition, yPosition, 300);
@ -505,9 +502,9 @@ public class GuiSkins extends GuiScreen {
rotate(((updateCounter + partialTick) * 2.5F) % 360, 0, 1, 0);
thePlayer.rotationYawHead = ((float) Math.atan(mouseX / 20)) * 30;
thePlayer.rotationYawHead = (float)Math.atan(mouseX / 20) * 30;
thePlayer.rotationPitch = (float)Math.atan(mouseY / 40) * -20;
thePlayer.rotationPitch = -((float) Math.atan(mouseY / 40)) * 20;
translate(0, thePlayer.getYOffset(), 0);
RenderManager rm = Minecraft.getMinecraft().getRenderManager();
@ -552,7 +549,7 @@ public class GuiSkins extends GuiScreen {
HDSkinManager.INSTANCE.getGatewayServer()
.uploadSkin(mc.getSession(), path, textureType, getMetadata())
.thenAccept(this::onUploadComplete)
.exceptionally(this::onFailure);
.exceptionally(this::onUploadFailure);
}
private Map<String, String> getMetadata() {
@ -564,8 +561,7 @@ public class GuiSkins extends GuiScreen {
btnUpload.enabled = true;
}
private Void onFailure(Throwable t) {
private Void onUploadFailure(Throwable t) {
t = Throwables.getRootCause(t);
LogManager.getLogger().warn("Upload failed", t);
setUploadError(t.toString());

View file

@ -16,6 +16,8 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import java.util.Set;
import static net.minecraft.client.renderer.GlStateManager.*;
@ -48,7 +50,7 @@ public class RenderPlayerModel<M extends EntityPlayerModel> extends RenderLiving
GlStateManager.enableBlend();
GlStateManager.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
bindTexture(entity.getElytraTexture());
bindTexture(entity.getLocal(Type.ELYTRA).getTexture());
GlStateManager.pushMatrix();
GlStateManager.translate(0, 0, 0.125F);
@ -70,7 +72,7 @@ public class RenderPlayerModel<M extends EntityPlayerModel> extends RenderLiving
@Override
protected ResourceLocation getEntityTexture(M entity) {
return entity.getSkinTexture();
return entity.getLocal(Type.SKIN).getTexture();
}
@Override

View file

@ -1,6 +1,7 @@
package com.minelittlepony.hdskins.gui;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.voxelmodpack.hdskins.gui.EntityPlayerModel;
import net.minecraft.util.ResourceLocation;
@ -20,19 +21,22 @@ public class EntityPonyModel extends EntityPlayerModel {
}
@Override
protected ResourceLocation getBlankSkin() {
public ResourceLocation getBlankSkin(Type type) {
if (type == Type.SKIN) {
return wet ? NO_SKIN_SEAPONY : NO_SKIN_PONY;
}
return super.getBlankSkin(type);
}
public void setWet(boolean wet) {
this.wet = wet;
if (wet && localSkinResource == NO_SKIN_PONY) {
localSkinResource = NO_SKIN_SEAPONY;
if (wet && skin.getTexture() == NO_SKIN_PONY) {
skin.reset();
}
if (!wet && localSkinResource == NO_SKIN_SEAPONY) {
localSkinResource = NO_SKIN_PONY;
if (!wet && skin.getTexture() == NO_SKIN_SEAPONY) {
skin.reset();
}
}
}

View file

@ -96,7 +96,7 @@ public class GuiSkinsMineLP extends GuiSkins {
protected void onSetLocalSkin(Type type) {
MineLittlePony.logger.debug("Invalidating old local skin, checking updated local skin");
if (type == Type.SKIN) {
ponyManager.removePony(localPlayer.getSkinTexture());
ponyManager.removePony(localPlayer.getLocal(Type.SKIN).getTexture());
}
}

View file

@ -8,6 +8,7 @@ import com.minelittlepony.model.player.PlayerModels;
import com.minelittlepony.pony.data.Pony;
import com.minelittlepony.pony.data.PonyRace;
import com.minelittlepony.render.layer.AbstractPonyLayer;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.voxelmodpack.hdskins.gui.RenderPlayerModel;
import net.minecraft.client.model.ModelBase;
@ -52,7 +53,7 @@ public class RenderPonyModel extends RenderPlayerModel<EntityPonyModel> {
return super.getEntityModel(playermodel);
}
boolean canWet = playermodel.wet && (loc == playermodel.getBlankSkin() || race == PonyRace.SEAPONY);
boolean canWet = playermodel.wet && (loc == playermodel.getBlankSkin(Type.SKIN) || race == PonyRace.SEAPONY);
ModelWrapper pm = canWet ? PlayerModels.SEAPONY.getModel(slim) : thePony.getModel(true);
pm.apply(thePony.getMetadata());
@ -75,7 +76,7 @@ public class RenderPonyModel extends RenderPlayerModel<EntityPonyModel> {
if (itemstack.getItem() == Items.ELYTRA) {
GlStateManager.color(1, 1, 1, 1);
bindTexture(entity.getElytraTexture());
bindTexture(entity.getLocal(Type.ELYTRA).getTexture());
GlStateManager.pushMatrix();