package net.mcft.copy.backpacks.client; import org.lwjgl.opengl.GL11; import net.minecraft.client.Minecraft; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.model.ModelRenderer; import net.minecraft.client.renderer.BlockModelRenderer; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.entity.RenderLivingBase; import net.minecraft.client.renderer.entity.layers.LayerRenderer; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHandSide; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.mcft.copy.backpacks.ProxyClient; import net.mcft.copy.backpacks.WearableBackpacks; import net.mcft.copy.backpacks.api.BackpackHelper; import net.mcft.copy.backpacks.api.BackpackRegistry; import net.mcft.copy.backpacks.api.IBackpack; import net.mcft.copy.backpacks.api.IBackpackType; import net.mcft.copy.backpacks.api.BackpackRegistry.BackpackEntityEntry; import net.mcft.copy.backpacks.api.BackpackRegistry.RenderOptions; import net.mcft.copy.backpacks.block.entity.TileEntityBackpack; import net.mcft.copy.backpacks.misc.util.IntermodUtils; /** Contains methods and nested classes which handle rendering of backpacks as tile entity and layer on regular entities. */ @SideOnly(Side.CLIENT) public final class RendererBackpack { private RendererBackpack() { } private static void renderBackpack(IBackpack backpack, float ticks, boolean renderStraps) { ItemStack stack = backpack.getStack(); int color = ProxyClient.ITEM_COLOR.colorMultiplier(stack, 0); BlockModelRenderer renderer = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelRenderer(); Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); float r = (color >> 16 & 0xFF) / 255.0F; float g = (color >> 8 & 0xFF) / 255.0F; float b = (color & 0xFF) / 255.0F; renderModel(backpack, renderer, ticks, renderStraps, r, g, b, false); if (stack.isItemEnchanted()) renderEnchanted(backpack, renderer, ticks, renderStraps); } // TODO: See if this can be changed back to FastTESR? // Forge apparently has an animation API? asie says to ask fry. public static class TileEntity extends TileEntitySpecialRenderer<TileEntityBackpack> { @Override public void render(TileEntityBackpack entity, double x, double y, double z, float partialTicks, int destroyStage, float alpha) { IBackpack backpack = BackpackHelper.getBackpack(entity); if (backpack == null) return; float angle = entity.facing.getHorizontalAngle(); GlStateManager.pushMatrix(); GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5); GlStateManager.rotate(angle, 0.0F, -1.0F, 0.0F); GlStateManager.translate(-0.5, -0.5, -0.5); renderBackpack(backpack, entity.getAge() + partialTicks, true); GlStateManager.popMatrix(); } } public static class Layer implements LayerRenderer<EntityLivingBase> { private static IBackpack _overrideBackpack = null; private static RenderOptions _overrideRenderOptions = null; protected final RenderLivingBase<?> livingEntityRenderer; public Layer(RenderLivingBase<?> livingEntityRendererIn) { this.livingEntityRenderer = livingEntityRendererIn; } public boolean shouldCombineTextures() { return false; } public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale) { IBackpack backpack; RenderOptions renderOptions; if (_overrideBackpack != null) { backpack = _overrideBackpack; renderOptions = _overrideRenderOptions; } else { backpack = BackpackHelper.getBackpack(entity); BackpackEntityEntry entry = BackpackRegistry.getEntityEntry(entity.getClass()); renderOptions = (entry != null) ? entry.renderOptions : null; } if ((backpack == null) || (renderOptions == null)) return; GlStateManager.pushMatrix(); if (entity.isChild()) { GlStateManager.scale(0.5F, 0.5F, 0.5F); GlStateManager.translate(0.0F, 24.0F * scale, 0.0F); } ModelBase modelBase = this.livingEntityRenderer.getMainModel(); if (modelBase instanceof ModelBiped) { ModelRenderer bipedBody = ((ModelBiped) modelBase).bipedBody; if (entity.isSneaking()) { // FIXME: Can be sneaking while flying with the elytra, then backpack is misaligned..? GlStateManager.translate(0.0F, 0.2F, 0.0F); } bipedBody.postRender(scale); } else { // Fallback to the custom way of transforming the backpack. // Make backpack swing with body as players swing their arms. float swingProgress = entity.getSwingProgress(partialTicks); float swingAngle = MathHelper.sin(MathHelper.sqrt(swingProgress) * ((float)Math.PI * 2.0F)) * 0.2F; if ((entity.swingingHand == EnumHand.OFF_HAND) ^ (entity.getPrimaryHand() == EnumHandSide.LEFT)) swingAngle *= -1; if (swingAngle != 0) GlStateManager.rotate((float)Math.toDegrees(swingAngle), 0.0F, 1.0F, 0.0F); // Rotate backpack if entity is sneaking. if (entity.isSneaking()) { // FIXME: Can be sneaking while flying with the elytra, then backpack is misaligned..? GlStateManager.translate(0.0F, 0.2F, 0.0F); GlStateManager.rotate(90.0F / (float)Math.PI, 1.0F, 0.0F, 0.0F); } } GlStateManager.scale(renderOptions.scale, renderOptions.scale, renderOptions.scale); GlStateManager.translate(8.0F * scale, renderOptions.y * scale, renderOptions.z * scale); GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F); GlStateManager.rotate((float)renderOptions.rotate, 1.0F, 0.0F, 0.0F); GlStateManager.translate(0, ProxyClient.MODEL_BACKPACK_BOX.maxY * scale * -16, ProxyClient.MODEL_BACKPACK_BOX.minZ * scale * -16); renderBackpack(backpack, entity.ticksExisted + partialTicks, false); GlStateManager.popMatrix(); } public static void setOverride(IBackpack backpack, RenderOptions renderOptions) { _overrideBackpack = backpack; _overrideRenderOptions = renderOptions; } public static void resetOverride() { setOverride(null, null); } } private static void renderModel(IBackpack backpack, BlockModelRenderer renderer, float ticks, boolean renderStraps, float r, float g, float b, boolean useEnch) { IBakedModel baseModel = (useEnch ? ProxyClient.MODEL_BACKPACK_ENCH : ProxyClient.MODEL_BACKPACK); renderer.renderModelBrightnessColor(baseModel, 1.0F, r, g, b); if (renderStraps) renderer.renderModelBrightnessColor(ProxyClient.MODEL_BACKPACK_STRAPS, 1.0F, r, g, b); float lidAngle = 0.0F; IBackpackType type = backpack.getType(); if (type != null) { int lidTicks = backpack.getLidTicks(); int prevLidTicks = backpack.getPrevLidTicks(); lidAngle = type.getLidAngle(lidTicks, prevLidTicks, ticks % 1); } // FIXME: Allow for custom, adjustable lid position. float lidYOffset = 9 / 16.0F; float lidZOffset = 5 / 16.0F; GlStateManager.pushMatrix(); GlStateManager.translate(0.0F, lidYOffset, lidZOffset); GlStateManager.rotate(lidAngle, -1.0F, 0.0F, 0.0F); GlStateManager.translate(0.0F, -lidYOffset, -lidZOffset); IBakedModel topModel = (useEnch ? ProxyClient.MODEL_BACKPACK_ENCH_TOP : ProxyClient.MODEL_BACKPACK_TOP); renderer.renderModelBrightnessColor(topModel, 1.0F, r, g, b); GlStateManager.popMatrix(); } private static final ResourceLocation ENCHANTED_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); // Based on LayerArmorBase.renderEnchantedGlint. private static void renderEnchanted(IBackpack backpack, BlockModelRenderer renderer, float ticks, boolean renderStraps) { Minecraft mc = Minecraft.getMinecraft(); float glintStrength = WearableBackpacks.CONFIG.cosmetic.enchantEffectOpacity.get().floatValue(); if (glintStrength <= 0) return; float glintScale = 1.5F; float animProgress = ticks / 10; int color = IntermodUtils.getRuneColor(backpack.getStack()); float r = (color >> 16 & 0xFF) / 255.0F * glintStrength; float g = (color >> 8 & 0xFF) / 255.0F * glintStrength; float b = (color & 0xFF) / 255.0F * glintStrength; mc.getTextureManager().bindTexture(ENCHANTED_ITEM_GLINT); mc.entityRenderer.setupFogColor(true); GlStateManager.disableLighting(); GlStateManager.depthMask(false); GlStateManager.depthFunc(GL11.GL_EQUAL); GlStateManager.enableBlend(); GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_COLOR, GlStateManager.DestFactor.ONE); for (int i = 0; i < 2; ++i) { GlStateManager.matrixMode(GL11.GL_TEXTURE); GlStateManager.loadIdentity(); GlStateManager.scale(glintScale, glintScale, glintScale); GlStateManager.rotate(30.0F - i * 60.0F, 0.0F, 0.0F, 1.0F); GlStateManager.translate(0.0F, animProgress * (0.001F + i * 0.003F) * 20.0F, 0.0F); GlStateManager.matrixMode(GL11.GL_MODELVIEW); renderModel(backpack, renderer, ticks, renderStraps, r, g, b, true); } GlStateManager.matrixMode(GL11.GL_TEXTURE); GlStateManager.loadIdentity(); GlStateManager.matrixMode(GL11.GL_MODELVIEW); GlStateManager.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); GlStateManager.disableBlend(); GlStateManager.depthFunc(GL11.GL_LEQUAL); GlStateManager.depthMask(true); GlStateManager.enableLighting(); mc.entityRenderer.setupFogColor(false); } }