From 2a32b74b5bb178b803a010e5796d5652acdc8156 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 11 Jun 2018 19:25:02 +0200 Subject: [PATCH] Implement proper tweening for certain animations --- .../model/AbstractPonyModel.java | 2 + .../model/anim/BasicEasingInterpolator.java | 30 +++++++++++++ .../model/anim/IInterpolator.java | 16 +++++++ .../model/anim/package-info.java | 7 +++ .../model/components/PegasusWings.java | 13 ++++-- .../model/components/PonyElytra.java | 2 +- .../minelittlepony/pony/data/IPonyData.java | 10 +++++ .../minelittlepony/pony/data/PonyData.java | 13 +++++- .../render/player/RenderPonyPlayer.java | 43 ++++++++----------- .../minelittlepony/util/math/MathUtil.java | 9 ++++ 10 files changed, 114 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/minelittlepony/model/anim/BasicEasingInterpolator.java create mode 100644 src/main/java/com/minelittlepony/model/anim/IInterpolator.java create mode 100644 src/main/java/com/minelittlepony/model/anim/package-info.java diff --git a/src/main/java/com/minelittlepony/model/AbstractPonyModel.java b/src/main/java/com/minelittlepony/model/AbstractPonyModel.java index b31db05f..fcb60536 100644 --- a/src/main/java/com/minelittlepony/model/AbstractPonyModel.java +++ b/src/main/java/com/minelittlepony/model/AbstractPonyModel.java @@ -224,6 +224,8 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel { float legRPX = cos - getLegOutset(); + legRPX = metadata.getInterpolator().interpolate("legOffset", legRPX, 3); + bipedRightArm.rotationPointX = -legRPX; bipedRightLeg.rotationPointX = -legRPX; diff --git a/src/main/java/com/minelittlepony/model/anim/BasicEasingInterpolator.java b/src/main/java/com/minelittlepony/model/anim/BasicEasingInterpolator.java new file mode 100644 index 00000000..2c23e362 --- /dev/null +++ b/src/main/java/com/minelittlepony/model/anim/BasicEasingInterpolator.java @@ -0,0 +1,30 @@ +package com.minelittlepony.model.anim; + +import java.util.HashMap; +import java.util.Map; + +public class BasicEasingInterpolator implements IInterpolator { + + private final Map properties = new HashMap(); + + private float getLast(String key, float to) { + if (properties.containsKey(key)) { + return properties.get(key); + } + + return to; + } + + @Override + public float interpolate(String key, float to, float scalingFactor) { + float from = getLast(key, to); + + from += (to - from) / scalingFactor; + + properties.put(key, from); + + return from; + + } + +} diff --git a/src/main/java/com/minelittlepony/model/anim/IInterpolator.java b/src/main/java/com/minelittlepony/model/anim/IInterpolator.java new file mode 100644 index 00000000..ad271b70 --- /dev/null +++ b/src/main/java/com/minelittlepony/model/anim/IInterpolator.java @@ -0,0 +1,16 @@ +package com.minelittlepony.model.anim; + +/** + * Interpolator function for handling transitions between animation states. + */ +@FunctionalInterface +public interface IInterpolator { + /** + * Interpolates a value between the requested final destination and what it was last. + * + * @param key Identifier to track previous values + * @param to The new values + * @param scalingFactor Scaling factor to control how quickly values change + */ + float interpolate(String key, float to, float scalingFactor); +} diff --git a/src/main/java/com/minelittlepony/model/anim/package-info.java b/src/main/java/com/minelittlepony/model/anim/package-info.java new file mode 100644 index 00000000..cfa76076 --- /dev/null +++ b/src/main/java/com/minelittlepony/model/anim/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package com.minelittlepony.model.anim; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/minelittlepony/model/components/PegasusWings.java b/src/main/java/com/minelittlepony/model/components/PegasusWings.java index 40602684..c4d27fbf 100644 --- a/src/main/java/com/minelittlepony/model/components/PegasusWings.java +++ b/src/main/java/com/minelittlepony/model/components/PegasusWings.java @@ -61,15 +61,22 @@ public class PegasusWings implement getLeft().rotateWalking(flap); getRight().rotateWalking(-flap); + float flapAngle = ROTATE_270; + if (pegasus.wingsAreOpen()) { - float flapAngle = pegasus.getWingRotationFactor(ticks); + flapAngle = pegasus.getWingRotationFactor(ticks); if (!pegasus.isCrouching() && pegasus.isWearing(PonyWearable.SADDLE_BAGS)) { flapAngle -= 1F; } - getLeft().rotateFlying(flapAngle); - getRight().rotateFlying(-flapAngle); } + if (!pegasus.isFlying()) { + flapAngle = pegasus.getMetadata().getInterpolator().interpolate("wingFlap", flapAngle, 10); + } + + getLeft().rotateFlying(flapAngle); + getRight().rotateFlying(-flapAngle); + } @Override diff --git a/src/main/java/com/minelittlepony/model/components/PonyElytra.java b/src/main/java/com/minelittlepony/model/components/PonyElytra.java index 85a460df..9c63f51e 100644 --- a/src/main/java/com/minelittlepony/model/components/PonyElytra.java +++ b/src/main/java/com/minelittlepony/model/components/PonyElytra.java @@ -19,7 +19,7 @@ public class PonyElytra extends ModelBase { private PonyRenderer leftWing = new PonyRenderer(this, 22, 0); public PonyElytra() { - leftWing .box(-10, 0, 0, 10, 20, 2, 1); + leftWing .box(-10, 0, 0, 10, 20, 2, 1); rightWing.flip().box( 0, 0, 0, 10, 20, 2, 1); } diff --git a/src/main/java/com/minelittlepony/pony/data/IPonyData.java b/src/main/java/com/minelittlepony/pony/data/IPonyData.java index 671e92ee..6fcaad7f 100644 --- a/src/main/java/com/minelittlepony/pony/data/IPonyData.java +++ b/src/main/java/com/minelittlepony/pony/data/IPonyData.java @@ -1,5 +1,7 @@ package com.minelittlepony.pony.data; +import com.minelittlepony.model.anim.IInterpolator; + import net.minecraft.client.resources.data.IMetadataSection; /** @@ -36,5 +38,13 @@ public interface IPonyData extends IMetadataSection { */ boolean hasMagic(); + /** + * Checks it this pony is wearing the given accessory. + */ boolean isWearing(PonyWearable wearable); + + /** + * Gets an interpolator for interpolating values. + */ + IInterpolator getInterpolator(); } diff --git a/src/main/java/com/minelittlepony/pony/data/PonyData.java b/src/main/java/com/minelittlepony/pony/data/PonyData.java index 74cd7eb3..1dfd3d74 100644 --- a/src/main/java/com/minelittlepony/pony/data/PonyData.java +++ b/src/main/java/com/minelittlepony/pony/data/PonyData.java @@ -2,6 +2,8 @@ package com.minelittlepony.pony.data; import com.google.common.base.MoreObjects; import com.minelittlepony.MineLittlePony; +import com.minelittlepony.model.anim.BasicEasingInterpolator; +import com.minelittlepony.model.anim.IInterpolator; import java.awt.image.BufferedImage; import javax.annotation.concurrent.Immutable; @@ -21,6 +23,8 @@ public class PonyData implements IPonyData { private final boolean[] wearables; + private final IInterpolator interpolator = new BasicEasingInterpolator(); + public PonyData() { race = PonyRace.HUMAN; tailSize = TailLengths.FULL; @@ -43,7 +47,7 @@ public class PonyData implements IPonyData { @Override public PonyRace getRace() { - return race; + return race.isHuman() ? race : PonyRace.PEGASUS; } @Override @@ -73,7 +77,7 @@ public class PonyData implements IPonyData { @Override public boolean isWearing(PonyWearable wearable) { - return wearables[wearable.ordinal()]; + return true;//wearables[wearable.ordinal()]; } @Override @@ -88,6 +92,11 @@ public class PonyData implements IPonyData { .toString(); } + @Override + public IInterpolator getInterpolator() { + return interpolator; + } + /** * Parses an image buffer into a new IPonyData representing the values stored in it's individual trigger pixels. */ diff --git a/src/main/java/com/minelittlepony/render/player/RenderPonyPlayer.java b/src/main/java/com/minelittlepony/render/player/RenderPonyPlayer.java index 1c26a817..5feb01b9 100644 --- a/src/main/java/com/minelittlepony/render/player/RenderPonyPlayer.java +++ b/src/main/java/com/minelittlepony/render/player/RenderPonyPlayer.java @@ -30,11 +30,22 @@ public class RenderPonyPlayer extends RenderPonyBase { GlStateManager.translate(0, player.isSneaking() ? 0.2F : -1, 0); } - private float sensibleAngle(float angle) { - angle %= 360; // if you need to copy-paste a portion of code more than 2 times - make a function - if (angle > 180) angle -= 360; - if (angle < -180) angle += 360; - return angle; + private double calculateRoll(AbstractClientPlayer player, double motionX, double motionY, double motionZ) { + + // since model roll should probably be calculated from model rotation rather than entity rotation... + double roll = MathUtil.sensibleAngle(player.prevRenderYawOffset - player.renderYawOffset); + double horMotion = Math.sqrt(motionX * motionX + motionZ * motionZ); + float modelYaw = MathUtil.sensibleAngle(player.renderYawOffset); + + // detecting that we're flying backwards and roll must be inverted + if (Math.abs(MathUtil.sensibleAngle((float) Math.toDegrees(Math.atan2(motionX, motionZ)) + modelYaw)) > 90) { + roll *= -1; + } + + // ayyy magic numbers (after 5 - an approximation of nice looking coefficients calculated by hand) + roll *= horMotion * 5 * (3.6884f * Math.pow(Math.abs(roll), -0.191)); + + return MathHelper.clamp(roll, -54, 54); } @Override @@ -56,27 +67,9 @@ public class RenderPonyPlayer extends RenderPonyBase { GlStateManager.rotate(ponyModel.motionPitch, 1, 0, 0); - double horMotion = Math.sqrt(motionX * motionX + motionZ * motionZ); + float roll = getPony().getMetadata().getInterpolator().interpolate("pegasusRoll", (float)calculateRoll(player, motionX, motionY, motionZ), 10); - if (horMotion > 0) { - yaw = sensibleAngle(player.cameraYaw - player.rotationYawHead); // will need later - // since model roll should probably be calculated from model rotation rather than entity rotation... - double roll = sensibleAngle(player.prevRenderYawOffset - player.renderYawOffset); - float modelYaw = sensibleAngle(player.renderYawOffset); - // filtering ugly jitter that occurs in Vanilla code if motion changes from sideways to diagonal - if (Math.abs(roll) > 0.5f && Math.abs(sensibleAngle(modelYaw + yaw)) > 40) { - return; - } - // detecting that we're flying backwards and roll must be inverted - if (Math.abs(sensibleAngle((float) Math.toDegrees(Math.atan2(motionX, motionZ)) + modelYaw)) > 90) { - roll *= -1; // because inline ifs are not in favor - } - // ayyy magic numbers (after 5 - an approximation of nice looking coefficients calculated by hand) - roll *= horMotion * 5 * (3.6884f * Math.pow(Math.abs(roll), -0.191)); - roll = MathHelper.clamp(roll, -54, 54); // safety measure, shouldn't be required anymore - - GlStateManager.rotate((float)roll, 0, 0, 1); - } + GlStateManager.rotate((float)roll, 0, 0, 1); } diff --git a/src/main/java/com/minelittlepony/util/math/MathUtil.java b/src/main/java/com/minelittlepony/util/math/MathUtil.java index 5dab16ec..2836b01b 100644 --- a/src/main/java/com/minelittlepony/util/math/MathUtil.java +++ b/src/main/java/com/minelittlepony/util/math/MathUtil.java @@ -7,4 +7,13 @@ public class MathUtil { public static double clampLimit(double num, double limit) { return MathHelper.clamp(num, -limit, limit); } + + public static float sensibleAngle(float angle) { + angle %= 360; + + if (angle > 180) angle -= 360; + if (angle < -180) angle += 360; + + return angle; + } }