package com.minelittlepony.model; import com.minelittlepony.model.armour.ModelPonyArmor; import com.minelittlepony.model.armour.PonyArmor; import com.minelittlepony.model.capabilities.IModel; import com.minelittlepony.model.components.PonySnout; import com.minelittlepony.model.components.PonyTail; import com.minelittlepony.pony.data.IPonyData; import com.minelittlepony.pony.data.PonyData; import com.minelittlepony.pony.data.PonySize; import com.minelittlepony.render.AbstractPonyRenderer; import com.minelittlepony.render.PonyRenderer; import com.minelittlepony.render.plane.PlaneRenderer; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelPlayer; import net.minecraft.client.model.ModelRenderer; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.EnumHandSide; import net.minecraft.util.math.MathHelper; import java.util.Random; import static net.minecraft.client.renderer.GlStateManager.*; import static com.minelittlepony.model.PonyModelConstants.*; /** * Foundation class for all types of ponies. */ public abstract class AbstractPonyModel extends ModelPlayer implements IModel { /** * The model's current scale. */ protected float scale = 0.0625F; public boolean isFlying; public boolean isSleeping; /** * Associcated pony data. */ public IPonyData metadata = new PonyData(); /** * Vertical pitch whilst flying. */ public float motionPitch; /** * Flag indicating that this model is performing a rainboom (flight). */ public boolean rainboom; public PlaneRenderer upperTorso; public PlaneRenderer neck; public PonyTail tail; public PonySnout snout; public AbstractPonyModel(boolean arms) { super(0, arms); } @Override public PonyArmor createArmour() { return new PonyArmor(new ModelPonyArmor(), new ModelPonyArmor()); } /** * Sets the model's various rotation angles. * * @param move Entity motion parameter - i.e. velocity in no specific direction used in bipeds to calculate step amount. * @param swing Degree to which each 'limb' swings. * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. * @param headYaw Horizontal head motion in radians. * @param headPitch Vertical head motion in radians. * @param scale Scaling factor used to render this model. Determined by the return value of {@link RenderLivingBase.prepareScale}. Usually {@code 0.0625F}. * @param entity The entity we're being called for. */ @Override public void setRotationAngles(float move, float swing, float ticks, float headYaw, float headPitch, float scale, Entity entity) { super.setRotationAngles(move, swing, ticks, headYaw, headPitch, scale, entity); float headRotateAngleY = isSleeping ? 1.4f : headYaw / 57.29578F; float headRotateAngleX = isSleeping ? 0.1f : headPitch / 57.29578F; headRotateAngleX = Math.min(headRotateAngleX, (float) (0.5f - Math.toRadians(motionPitch))); headRotateAngleX = Math.max(headRotateAngleX, (float) (-1.25f - Math.toRadians(motionPitch))); updateHeadRotation(headRotateAngleX, headRotateAngleY); float bodySwingRotation = 0; if (swingProgress > -9990.0F && !metadata.hasMagic()) { bodySwingRotation = MathHelper.sin(MathHelper.sqrt(swingProgress) * PI * 2) * 0.2F; } rotateLook(move, swing, bodySwingRotation, ticks); setLegs(move, swing, ticks, entity); holdItem(swing); swingItem(entity); if (isCrouching()) { adjustBody(BODY_ROTATE_ANGLE_X_SNEAK, BODY_RP_Y_SNEAK, BODY_RP_Z_SNEAK); sneakLegs(); setHead(0, 6, -2); } else if (isRiding) { adjustBodyRiding(); bipedLeftLeg.rotationPointZ = 15; bipedLeftLeg.rotationPointY = 10; bipedLeftLeg.rotateAngleX = -PI / 4; bipedLeftLeg.rotateAngleY = -PI / 5; bipedRightLeg.rotationPointZ = 15; bipedRightLeg.rotationPointY = 10; bipedRightLeg.rotateAngleX = -PI / 4; bipedRightLeg.rotateAngleY = PI / 5; bipedLeftArm.rotateAngleZ = -PI * 0.06f; bipedRightArm.rotateAngleZ = PI * 0.06f; } else { adjustBody(BODY_ROTATE_ANGLE_X_NOTSNEAK, BODY_RP_Y_NOTSNEAK, BODY_RP_Z_NOTSNEAK); bipedRightLeg.rotationPointY = FRONT_LEG_RP_Y_NOTSNEAK; bipedLeftLeg.rotationPointY = FRONT_LEG_RP_Y_NOTSNEAK; swingArms(ticks); setHead(0, 0, 0); } if (isSleeping) ponySleep(); aimBow(leftArmPose, rightArmPose, ticks); fixSpecialRotationPoints(move); animateWears(); if (snout != null) { snout.setGender(metadata.getGender()); } } protected void adjustBodyRiding() { adjustBodyComponents(BODY_ROTATE_ANGLE_X_RIDING, BODY_RP_Y_RIDING, BODY_RP_Z_RIDING); adjustNeck(BODY_ROTATE_ANGLE_X_NOTSNEAK, BODY_RP_Y_NOTSNEAK, BODY_RP_Z_NOTSNEAK); setHead(0, 0, 0); } /** * Sets the model's various rotation angles. * * @param move Entity motion parameter - i.e. velocity in no specific direction used in bipeds to calculate step amount. * @param swing Degree to which each 'limb' swings. * @param bodySwing Horizontal (Y) body rotation. * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. */ protected void rotateLook(float move, float swing, float bodySwing, float ticks) { tail.setRotationAndAngles(rainboom, move, swing, bodySwing, ticks); bodySwing /= 5; upperTorso.rotateAngleY = bodySwing; bipedBody.rotateAngleY = bodySwing; neck.rotateAngleY = bodySwing; } private void animateWears() { copyModelAngles(bipedLeftArm, bipedLeftArmwear); copyModelAngles(bipedRightArm, bipedRightArmwear); copyModelAngles(bipedLeftLeg, bipedLeftLegwear); copyModelAngles(bipedRightLeg, bipedRightLegwear); copyModelAngles(bipedBody, bipedBodyWear); } /** * Sets the head rotation point. */ protected void setHead(float posX, float posY, float posZ) { bipedHead.setRotationPoint(posX, posY, posZ); bipedHeadwear.setRotationPoint(posX, posY, posZ); } /** * Called to update the head rotation. * * @param x New rotation X * @param y New rotation Y */ protected void updateHeadRotation(float x, float y) { bipedHeadwear.rotateAngleY = bipedHead.rotateAngleY = y; bipedHeadwear.rotateAngleX = bipedHead.rotateAngleX = x; } /** * * Used to set the legs rotation based on walking/crouching animations. * * Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles} * * TODO: This can be merged into adjustLegs * */ protected void setLegs(float move, float swing, float ticks, Entity entity) { if (isFlying(entity)) { rotateLegsInFlight(move, swing, ticks, entity); } else { rotateLegsOnGround(move, swing, ticks, entity); } bipedLeftArm.rotateAngleZ = 0; bipedRightArm.rotateAngleZ = 0; adjustLegs(move, swing, ticks); } /** * Rotates legs in quopy fashion whilst flying. * * @param move Entity motion parameter - i.e. velocity in no specific direction used in bipeds to calculate step amount. * @param swing Degree to which each 'limb' swings. * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. * @param entity The entity we're being called for. * */ protected void rotateLegsInFlight(float move, float swing, float ticks, Entity entity) { float armX = MathHelper.sin(-swing / 2); float legX = MathHelper.sin(swing / 2); bipedLeftArm.rotateAngleX = armX; bipedRightArm.rotateAngleX = armX; bipedLeftLeg.rotateAngleX = legX; bipedRightLeg.rotateAngleX = legX; bipedLeftArm.rotateAngleY = -0.2F; bipedLeftLeg.rotateAngleY = 0.2F; bipedRightArm.rotateAngleY = 0.2F; bipedRightLeg.rotateAngleY = -0.2F; } /** * Rotates legs in quopy fashion for walking. * * @param move Entity motion parameter - i.e. velocity in no specific direction used in bipeds to calculate step amount. * @param swing Degree to which each 'limb' swings. * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. * @param entity The entity we're being called for. * */ protected void rotateLegsOnGround(float move, float swing, float ticks, Entity entity) { float angle = PI * (float) Math.pow(swing, 16); float baseRotation = move * 0.6662F; // magic number ahoy float scale = swing / 4; bipedLeftArm.rotateAngleX = MathHelper.cos(baseRotation + angle) * scale; bipedRightArm.rotateAngleX = MathHelper.cos(baseRotation + PI + angle / 2) * scale; bipedLeftLeg.rotateAngleX = MathHelper.cos(baseRotation + PI - (angle * 0.4f)) * scale; bipedRightLeg.rotateAngleX = MathHelper.cos(baseRotation + angle / 5) * scale; bipedLeftArm.rotateAngleY = 0; bipedRightArm.rotateAngleY = 0; bipedLeftLeg.rotateAngleY = 0; bipedRightLeg.rotateAngleY = 0; } protected float getLegOutset() { if (isSleeping) return 3.6f; if (isCrouching()) return 1; return 5; } protected float getLegSpread() { return rainboom ? 2 : 1; } /** * * Used to set the legs rotation based on walking/crouching animations. * * Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles} * * TODO: This can be merged into setLegs * */ protected void adjustLegs(float move, float swing, float ticks) { float sin = MathHelper.sin(bipedBody.rotateAngleY) * 5; float cos = MathHelper.cos(bipedBody.rotateAngleY) * 5; float spread = getLegSpread(); bipedRightArm.rotationPointZ = spread + sin; bipedLeftArm.rotationPointZ = spread - sin; float legRPX = cos - getLegOutset(); bipedRightArm.rotationPointX = -legRPX; bipedRightLeg.rotationPointX = -legRPX; bipedLeftArm.rotationPointX = legRPX; bipedLeftLeg.rotationPointX = legRPX; bipedRightArm.rotateAngleY += bipedBody.rotateAngleY; bipedLeftArm.rotateAngleY += bipedBody.rotateAngleY; bipedRightArm.rotationPointY = bipedLeftArm.rotationPointY = 8; bipedRightLeg.rotationPointZ = bipedLeftLeg.rotationPointZ = 10; } /** * Adjusts legs as if holding an item. Delegates to the correct arm/leg/limb as neccessary. * * @param swing */ protected void holdItem(float swing) { boolean both = leftArmPose == ArmPose.ITEM && rightArmPose == ArmPose.ITEM; alignArmForAction(bipedLeftArm, leftArmPose, both, swing); alignArmForAction(bipedRightArm, rightArmPose, both, swing); } /** * Aligns an arm for the appropriate arm pose * * @param arm The arm model to align * @param pose The post to align to * @param both True if we have something in both hands * @param swing Degree to which each 'limb' swings. */ protected void alignArmForAction(ModelRenderer arm, ArmPose pose, boolean both, float swing) { switch (pose) { case ITEM: float swag = 1; if (!isFlying && both) { swag -= (float)Math.pow(swing, 2); } float mult = 1 - swag/2; arm.rotateAngleX = bipedLeftArm.rotateAngleX * mult - (PI / 10) * swag; case EMPTY: arm.rotateAngleY = 0; break; case BLOCK: arm.rotateAngleX = arm.rotateAngleX / 2 - 0.9424779F; arm.rotateAngleY = PI / 6; break; default: } } /** * Animates arm swinging. Delegates to the correct arm/leg/limb as neccessary. * * @param entity The entity we are being called for. */ protected void swingItem(Entity entity) { if (swingProgress > -9990.0F && !isSleeping) { EnumHandSide mainSide = getMainHand(entity); swingArm(getArmForSide(mainSide)); } } /** * Animates arm swinging. * * @param arm The arm to swing */ protected void swingArm(ModelRenderer arm) { float swing = 1 - (float)Math.pow(1 - swingProgress, 3); float deltaX = MathHelper.sin(swing * PI); float deltaZ = MathHelper.sin(swingProgress * PI); float deltaAim = deltaZ * (0.7F - bipedHead.rotateAngleX) * 0.75F; arm.rotateAngleX -= deltaAim + deltaX * 1.2F; arm.rotateAngleY += bipedBody.rotateAngleY * 2; arm.rotateAngleZ = -deltaZ * 0.4F; } /** * Animates the walking animation. * * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. */ protected void swingArms(float ticks) { if (isSleeping) return; float cos = MathHelper.cos(ticks * 0.09F) * 0.05F + 0.05F; float sin = MathHelper.sin(ticks * 0.067F) * 0.05F; if (rightArmPose != ArmPose.EMPTY) { bipedRightArm.rotateAngleZ += cos; bipedRightArm.rotateAngleX += sin; } if (leftArmPose != ArmPose.EMPTY) { bipedLeftArm.rotateAngleZ += cos; bipedLeftArm.rotateAngleX += sin; } } protected void adjustBody(float rotateAngleX, float rotationPointY, float rotationPointZ) { adjustBodyComponents(rotateAngleX, rotationPointY, rotationPointZ); adjustNeck(rotateAngleX, rotationPointY, rotationPointZ); } protected void adjustBodyComponents(float rotateAngleX, float rotationPointY, float rotationPointZ) { bipedBody.rotateAngleX = rotateAngleX; bipedBody.rotationPointY = rotationPointY; bipedBody.rotationPointZ = rotationPointZ; upperTorso.rotateAngleX = rotateAngleX; upperTorso.rotationPointY = rotationPointY; upperTorso.rotationPointZ = rotationPointZ; } protected void adjustNeck(float rotateAngleX, float rotationPointY, float rotationPointZ) { neck.setRotationPoint(NECK_ROT_X + rotateAngleX, rotationPointY, rotationPointZ); } /** * Aligns legs to a sneaky position. */ protected void sneakLegs() { bipedRightArm.rotateAngleX -= SNEAK_LEG_X_ROTATION_ADJUSTMENT; bipedLeftArm.rotateAngleX -= SNEAK_LEG_X_ROTATION_ADJUSTMENT; bipedLeftLeg.rotationPointY = bipedRightLeg.rotationPointY = FRONT_LEG_RP_Y_SNEAK; } protected void ponySleep() { bipedRightArm.rotateAngleX = ROTATE_270; bipedLeftArm.rotateAngleX = ROTATE_270; bipedRightLeg.rotateAngleX = ROTATE_90; bipedLeftLeg.rotateAngleX = ROTATE_90; setHead(1, 2, isSneak ? -1 : 1); AbstractPonyRenderer.shiftRotationPoint(bipedRightArm, 0, 2, 6); AbstractPonyRenderer.shiftRotationPoint(bipedLeftArm, 0, 2, 6); AbstractPonyRenderer.shiftRotationPoint(bipedRightLeg, 0, 2, -8); AbstractPonyRenderer.shiftRotationPoint(bipedLeftLeg, 0, 2, -8); } protected void aimBow(ArmPose leftArm, ArmPose rightArm, float ticks) { if (rightArm == ArmPose.BOW_AND_ARROW) aimBowPony(bipedRightArm, ticks); if (leftArm == ArmPose.BOW_AND_ARROW) aimBowPony(bipedLeftArm, ticks); } protected void aimBowPony(ModelRenderer arm, float ticks) { arm.rotateAngleZ = 0; arm.rotateAngleY = bipedHead.rotateAngleY - 0.06F; arm.rotateAngleX = ROTATE_270 + bipedHead.rotateAngleX; arm.rotateAngleZ += MathHelper.cos(ticks * 0.09F) * 0.05F + 0.05F; arm.rotateAngleX += MathHelper.sin(ticks * 0.067F) * 0.05F; } /** * Called after postioning but before wears alignment to perform some last-minute adjustments. * * @param move Entity motion parameter. See {@link AbstractPonyModel.setRotationAngles}. * * TODO: Empty method */ protected void fixSpecialRotationPoints(float move) { } public void init(float yOffset, float stretch) { // TODO: Splitting things like this isn't strictly neccessary and just complicates things. initTextures(); initPositions(yOffset, stretch); } /** * Loads texture values. */ protected void initTextures() { boxList.clear(); initHeadTextures(); initBodyTextures(); initLegTextures(); initTailTextures(); } /** * Loads texture positions and boxes. Pretty much just finishes the job of initTextures. */ protected void initPositions(float yOffset, float stretch) { initHeadPositions(yOffset, stretch); initBodyPositions(yOffset, stretch); initLegPositions(yOffset, stretch); initTailPositions(yOffset, stretch); } protected void initTailTextures() { tail = new PonyTail(this); } protected void initHeadTextures() { bipedHead = new PonyRenderer(this, 0, 0); bipedHeadwear = new PonyRenderer(this, 32, 0); snout = new PonySnout(this); } protected void initBodyTextures() { bipedBody = new ModelRenderer(this, 16, 16); if (textureHeight == 64) { bipedBodyWear = new ModelRenderer(this, 16, 32); } upperTorso = new PlaneRenderer(this, 24, 0); neck = new PlaneRenderer(this, 0, 16); } protected void initLegTextures() { bipedLeftArm = new ModelRenderer(this, 32, 48); bipedRightArm = new ModelRenderer(this, 40, 16); bipedLeftArmwear = new ModelRenderer(this, 48, 48); bipedRightArmwear = new ModelRenderer(this, 40, 32); bipedLeftLeg = new ModelRenderer(this, 16, 48); bipedRightLeg = new ModelRenderer(this, 0, 16); bipedLeftLegwear = new ModelRenderer(this, 0, 48); bipedRightLegwear = new ModelRenderer(this, 0, 32); } protected void initTailPositions(float yOffset, float stretch) { tail.init(yOffset, stretch); } protected void initHeadPositions(float yOffset, float stretch) { snout.init(yOffset, stretch); ((PonyRenderer)bipedHead).offset(HEAD_CENTRE_X, HEAD_CENTRE_Y, HEAD_CENTRE_Z) .around(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z - 2) .box(-4, -4, -4, 8, 8, 8, stretch) .tex(12, 16) .box(-4, -6, 1, 2, 2, 2, stretch) .flipX() .box(2, -6, 1, 2, 2, 2, stretch); ((PonyRenderer)bipedHeadwear).offset(HEAD_CENTRE_X, HEAD_CENTRE_Y, HEAD_CENTRE_Z) .around(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z - 2) .box(-4, -4, -4, 8, 8, 8, stretch + 0.5F); } /** * Creates the main torso and neck. */ protected void initBodyPositions(float yOffset, float stretch) { bipedBody.addBox(-4, 4, -2, 8, 8, 4, stretch); bipedBody.setRotationPoint(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z); bipedBodyWear.addBox(-4, 4, -2, 8, 8, 4, stretch + 0.25F); bipedBodyWear.setRotationPoint(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z); upperTorso.offset(BODY_CENTRE_X, BODY_CENTRE_Y, BODY_CENTRE_Z) .around(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z) .tex(24, 0) .addEastPlane( 4, -4, -4, 8, 8, stretch) .tex(4, 0) .addEastPlane( 4, -4, 4, 8, 4, stretch) .tex(56, 0) .addBottomPlane(-4, 4, -4, 8, 8, stretch) .tex(36, 16) .addBackPlane(-4, -4, 8, 8, 4, stretch) .addBackPlane(-4, 0, 8, 8, 4, stretch) .addBottomPlane(-4, 4, 4, 8, 4, stretch) .flipZ().tex(32, 20).addTopPlane(-4, -4, -4, 8, 12, stretch) .tex(24, 0).addWestPlane(-4, -4, -4, 8, 8, stretch) .tex(4, 0) .addWestPlane(-4, -4, 4, 8, 4, stretch) // Tail stub .child(0) .tex(32, 0).addTopPlane(-1, 2, 2, 2, 6, stretch) .addBottomPlane(-1, 4, 2, 2, 6, stretch) .addEastPlane( 1, 2, 2, 2, 6, stretch) .addBackPlane(-1, 2, 8, 2, 2, stretch) .flipZ().addWestPlane(-1, 2, 2, 2, 6, stretch) .rotateAngleX = 0.5F; neck.at(NECK_CENTRE_X, NECK_CENTRE_Y, NECK_CENTRE_Z) .around(HEAD_RP_X, HEAD_RP_Y + yOffset, HEAD_RP_Z) .addFrontPlane(0, 0, 0, 4, 4, stretch) .addBackPlane(0, 0, 4, 4, 4, stretch) .addEastPlane(4, 0, 0, 4, 4, stretch) .addWestPlane(0, 0, 0, 4, 4, stretch) .rotateAngleX = NECK_ROT_X; } protected int getArmWidth() { return 4; } protected int getArmDepth() { return 4; } protected float getLegRotationX() { return 3; } protected float getArmRotationY() { return 8; } protected void initLegPositions(float yOffset, float stretch) { int armWidth = getArmWidth(); int armDepth = getArmDepth(); float rarmX = getLegRotationX(); float rarmY = getArmRotationY(); float armX = THIRDP_ARM_CENTRE_X; float armY = THIRDP_ARM_CENTRE_Y - 6; float armZ = BODY_CENTRE_Z / 2 - 1 - armDepth; bipedLeftArm .addBox(armX, armY, armZ, armWidth, 12, armDepth, stretch); bipedRightArm.addBox(armX - armWidth, armY, armZ, armWidth, 12, armDepth, stretch); bipedLeftLeg .addBox(armX, armY, armZ, armWidth, 12, armDepth, stretch); bipedRightLeg.addBox(armX - armWidth, armY, armZ, armWidth, 12, armDepth, stretch); bipedLeftArm .setRotationPoint( rarmX, yOffset + rarmY, 0); bipedRightArm.setRotationPoint(-rarmX, yOffset + rarmY, 0); bipedLeftLeg .setRotationPoint( rarmX, yOffset, 0); bipedRightLeg.setRotationPoint(-rarmX, yOffset, 0); if (bipedLeftArmwear != null) { bipedLeftArmwear.addBox(armX, armY, armZ, armWidth, 12, armDepth, stretch + 0.25f); bipedLeftArmwear.setRotationPoint(rarmX, yOffset + rarmY, 0); } if (bipedRightArmwear != null) { bipedRightArmwear.addBox(armX - armWidth, armY, armZ, armWidth, 12, armDepth, stretch + 0.25f); bipedRightArmwear.setRotationPoint(-rarmX, yOffset + rarmY, 0); } if (bipedLeftLegwear != null) { bipedLeftLegwear.addBox(armX, armY, armZ, armWidth, 12, armDepth, stretch + 0.25f); bipedRightLegwear.setRotationPoint(rarmX, yOffset, 0); } if (bipedRightLegwear != null) { bipedRightLegwear.addBox(armX - armWidth, armY, armZ, armWidth, 12, armDepth, stretch + 0.25f); bipedRightLegwear.setRotationPoint(-rarmX, yOffset, 0); } } public ArmPose getArmPoseForSide(EnumHandSide side) { return side == EnumHandSide.RIGHT ? rightArmPose : leftArmPose; } @Override public boolean isCrouching() { return isSneak && !isFlying; } /** * Checks flying and speed conditions and sets rainboom to true if we're a species with wings and is going faaast. */ protected void checkRainboom(Entity entity, float swing) { rainboom = isFlying(entity) && swing >= 0.9999F; } @Override public boolean isFlying(Entity entity) { return (isFlying && metadata.getRace().hasWings()) || (entity instanceof EntityLivingBase && ((EntityLivingBase) entity).isElytraFlying()); } @Override public boolean isFlying() { return isFlying; } @Override public boolean isChild() { return metadata.getSize() == PonySize.FOAL || isChild; } @Override public float getSwingAmount() { return swingProgress; } /** * Rotates the provided arm to the correct orientation for holding an item. * * @param arm The arm to rotate * @param direction Direction multiplier. 1 for right, -1 for left. * @param swingProgress How far we are through the current swing * @param ticks Render partial ticks */ protected void rotateArmHolding(ModelRenderer arm, float direction, float swingProgress, float ticks) { float swing = MathHelper.sin(swingProgress * PI); float roll = MathHelper.sin((1 - (1 - swingProgress) * (1 - swingProgress)) * PI); float cos = MathHelper.cos(ticks * 0.09F) * 0.05F + 0.05F; float sin = MathHelper.sin(ticks * 0.067F) / 10; arm.rotateAngleX = -1.5707964F; arm.rotateAngleX -= swing * 1.2F - roll * 0.4F; arm.rotateAngleX += sin; arm.rotateAngleY = direction * (0.1F - swing * 0.6F); arm.rotateAngleZ = cos; } /** * Sets the model's various rotation angles. * * @param entity The entity we're being called for. * @param move Entity motion parameter - i.e. velocity in no specific direction used in bipeds to calculate step amount. * @param swing Degree to which each 'limb' swings. * @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}. * @param headYaw Horizontal head motion in radians. * @param headPitch Vertical head motion in radians. * @param scale Scaling factor used to render this model. Determined by the return value of {@link RenderLivingBase.prepareScale}. Usually {@code 0.0625F}. */ @Override public void render(Entity entityIn, float move, float swing, float ticks, float headYaw, float headPitch, float scale) { pushMatrix(); transform(BodyPart.HEAD); renderHead(entityIn, move, swing, ticks, headYaw, headPitch, this.scale); popMatrix(); pushMatrix(); transform(BodyPart.NECK); renderNeck(); popMatrix(); pushMatrix(); transform(BodyPart.BODY); renderBody(entityIn, move, swing, ticks, headYaw, headPitch, this.scale); popMatrix(); pushMatrix(); transform(BodyPart.LEGS); renderLegs(); popMatrix(); } /** * * Called to render the head. * * Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles} * */ protected void renderHead(Entity entity, float move, float swing, float ticks, float headYaw, float headPitch, float scale) { bipedHead.render(scale); bipedHeadwear.render(scale); bipedHead.postRender(scale); } protected void renderNeck() { GlStateManager.scale(0.9, 0.9, 0.9); neck.render(scale); } /** * * Called to render the head. * * Takes the same parameters as {@link AbstractPonyModel.setRotationAndAngles} * */ protected void renderBody(Entity entity, float move, float swing, float ticks, float headYaw, float headPitch, float scale) { bipedBody.render(scale); if (textureHeight == 64) { bipedBodyWear.render(scale); } upperTorso.render(scale); bipedBody.postRender(scale); tail.render(scale); } protected void renderLegs() { if (!isSneak) bipedBody.postRender(scale); bipedLeftArm.render(scale); bipedRightArm.render(scale); bipedLeftLeg.render(scale); bipedRightLeg.render(scale); if (textureHeight == 64) { bipedLeftArmwear.render(scale); bipedRightArmwear.render(scale); bipedLeftLegwear.render(scale); bipedRightLegwear.render(scale); } } @Override public void transform(BodyPart part) { if (isRiding) translate(0, -0.6F, -0.2F); if (isSleeping) { rotate(90, 1, 0, 0); rotate(180, 0, 1, 0); } if (part == BodyPart.HEAD) { rotate(motionPitch, 1, 0, 0); } // TODO: Get these out of here if (isChild()) { transformFoal(part); } else if (metadata.getSize() == PonySize.LARGE) { transformLarge(part); } else if (metadata.getSize() == PonySize.TALL) { transformTall(part); } else { if (isSleeping) translate(0, -0.61F, 0.25F); } } private void transformTall(BodyPart part) { if (isSleeping) translate(0, -0.5F, 0.25F); switch (part) { case HEAD: translate(0, -0.15F, 0.01F); if (isCrouching()) translate(0, 0.05F, 0); break; case NECK: translate(0, -0.19F, -0.01F); scale(1, 1.1F, 1); if (isCrouching()) translate(0, -0.06F, -0.04F); break; case BODY: case TAIL: translate(0, -0.1F, 0); scale(1, 1, 1); break; case LEGS: translate(0, -0.25F, 0.03F); scale(1, 1.18F, 1); if (rainboom) translate(0, 0.05F, 0); break; } } private void transformLarge(BodyPart part) { if (isSleeping) translate(0, -0.98F, 0.2F); switch (part) { case HEAD: translate(0, -0.17F, -0.04F); if (isSleeping) translate(0, 0, -0.1F); if (isCrouching()) translate(0, 0.15F, 0); break; case NECK: translate(0, -0.15F, -0.07F); if (isCrouching()) translate(0, 0, -0.05F); break; case BODY: translate(0, -0.2F, -0.04F); scale(1.15F, 1.2F, 1.2F); break; case TAIL: translate(0, -0.2F, 0.08F); break; case LEGS: translate(0, -0.14F, 0); scale(1.15F, 1.12F, 1.15F); break; } } private void transformFoal(BodyPart part) { if (isCrouching()) translate(0, -0.12F, 0); if (isSleeping) translate(0, -1.48F, 0.25F); if (isRiding) translate(0, -0.1F, 0); switch (part) { case NECK: case HEAD: translate(0, 0.76F, 0); scale(0.9F, 0.9F, 0.9F); if (part == BodyPart.HEAD) break; if (isCrouching()) translate(0, -0.01F, 0.15F); break; case BODY: case TAIL: translate(0, 0.76F, -0.04F); scale(0.6F, 0.6F, 0.6F); break; case LEGS: translate(0, 0.89F, 0); scale(0.6F, 0.41F, 0.6F); if (isCrouching()) translate(0, 0.12F, 0); if (rainboom) translate(0, -0.08F, 0); break; } } /** * Copies this model's attributes from some other. */ @Override public void setModelAttributes(ModelBase model) { super.setModelAttributes(model); if (model instanceof AbstractPonyModel) { AbstractPonyModel pony = (AbstractPonyModel) model; isFlying = pony.isFlying; isSleeping = pony.isSleeping; metadata = pony.metadata; motionPitch = pony.motionPitch; rainboom = pony.rainboom; } } @Override public ModelRenderer getRandomModelBox(Random rand) { // grab one at random, but cycle through the list until you find one that's filled. // Return if you find one, or if you get back to where you started in which case there isn't any. int randomI = rand.nextInt(boxList.size()); int index = randomI; ModelRenderer result; do { result = boxList.get(randomI); if (!result.cubeList.isEmpty()) return result; index = (index + 1) % boxList.size(); } while (index != randomI); return result; } }