Implement proper tweening for certain animations

This commit is contained in:
Sollace 2018-06-11 19:25:02 +02:00
parent 845f3536ad
commit 2a32b74b5b
10 changed files with 114 additions and 31 deletions

View file

@ -224,6 +224,8 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel {
float legRPX = cos - getLegOutset(); float legRPX = cos - getLegOutset();
legRPX = metadata.getInterpolator().interpolate("legOffset", legRPX, 3);
bipedRightArm.rotationPointX = -legRPX; bipedRightArm.rotationPointX = -legRPX;
bipedRightLeg.rotationPointX = -legRPX; bipedRightLeg.rotationPointX = -legRPX;

View file

@ -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<String, Float> properties = new HashMap<String, Float>();
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;
}
}

View file

@ -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);
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package com.minelittlepony.model.anim;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -61,15 +61,22 @@ public class PegasusWings<T extends AbstractPonyModel & IModelPegasus> implement
getLeft().rotateWalking(flap); getLeft().rotateWalking(flap);
getRight().rotateWalking(-flap); getRight().rotateWalking(-flap);
float flapAngle = ROTATE_270;
if (pegasus.wingsAreOpen()) { if (pegasus.wingsAreOpen()) {
float flapAngle = pegasus.getWingRotationFactor(ticks); flapAngle = pegasus.getWingRotationFactor(ticks);
if (!pegasus.isCrouching() && pegasus.isWearing(PonyWearable.SADDLE_BAGS)) { if (!pegasus.isCrouching() && pegasus.isWearing(PonyWearable.SADDLE_BAGS)) {
flapAngle -= 1F; 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 @Override

View file

@ -19,7 +19,7 @@ public class PonyElytra extends ModelBase {
private PonyRenderer leftWing = new PonyRenderer(this, 22, 0); private PonyRenderer leftWing = new PonyRenderer(this, 22, 0);
public PonyElytra() { 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); rightWing.flip().box( 0, 0, 0, 10, 20, 2, 1);
} }

View file

@ -1,5 +1,7 @@
package com.minelittlepony.pony.data; package com.minelittlepony.pony.data;
import com.minelittlepony.model.anim.IInterpolator;
import net.minecraft.client.resources.data.IMetadataSection; import net.minecraft.client.resources.data.IMetadataSection;
/** /**
@ -36,5 +38,13 @@ public interface IPonyData extends IMetadataSection {
*/ */
boolean hasMagic(); boolean hasMagic();
/**
* Checks it this pony is wearing the given accessory.
*/
boolean isWearing(PonyWearable wearable); boolean isWearing(PonyWearable wearable);
/**
* Gets an interpolator for interpolating values.
*/
IInterpolator getInterpolator();
} }

View file

@ -2,6 +2,8 @@ package com.minelittlepony.pony.data;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.minelittlepony.MineLittlePony; import com.minelittlepony.MineLittlePony;
import com.minelittlepony.model.anim.BasicEasingInterpolator;
import com.minelittlepony.model.anim.IInterpolator;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@ -21,6 +23,8 @@ public class PonyData implements IPonyData {
private final boolean[] wearables; private final boolean[] wearables;
private final IInterpolator interpolator = new BasicEasingInterpolator();
public PonyData() { public PonyData() {
race = PonyRace.HUMAN; race = PonyRace.HUMAN;
tailSize = TailLengths.FULL; tailSize = TailLengths.FULL;
@ -43,7 +47,7 @@ public class PonyData implements IPonyData {
@Override @Override
public PonyRace getRace() { public PonyRace getRace() {
return race; return race.isHuman() ? race : PonyRace.PEGASUS;
} }
@Override @Override
@ -73,7 +77,7 @@ public class PonyData implements IPonyData {
@Override @Override
public boolean isWearing(PonyWearable wearable) { public boolean isWearing(PonyWearable wearable) {
return wearables[wearable.ordinal()]; return true;//wearables[wearable.ordinal()];
} }
@Override @Override
@ -88,6 +92,11 @@ public class PonyData implements IPonyData {
.toString(); .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. * Parses an image buffer into a new IPonyData representing the values stored in it's individual trigger pixels.
*/ */

View file

@ -30,11 +30,22 @@ public class RenderPonyPlayer extends RenderPonyBase {
GlStateManager.translate(0, player.isSneaking() ? 0.2F : -1, 0); GlStateManager.translate(0, player.isSneaking() ? 0.2F : -1, 0);
} }
private float sensibleAngle(float angle) { private double calculateRoll(AbstractClientPlayer player, double motionX, double motionY, double motionZ) {
angle %= 360; // if you need to copy-paste a portion of code more than 2 times - make a function
if (angle > 180) angle -= 360; // since model roll should probably be calculated from model rotation rather than entity rotation...
if (angle < -180) angle += 360; double roll = MathUtil.sensibleAngle(player.prevRenderYawOffset - player.renderYawOffset);
return angle; 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 @Override
@ -56,27 +67,9 @@ public class RenderPonyPlayer extends RenderPonyBase {
GlStateManager.rotate(ponyModel.motionPitch, 1, 0, 0); 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) { GlStateManager.rotate((float)roll, 0, 0, 1);
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);
}
} }

View file

@ -7,4 +7,13 @@ public class MathUtil {
public static double clampLimit(double num, double limit) { public static double clampLimit(double num, double limit) {
return MathHelper.clamp(num, -limit, 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;
}
} }