mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
Merge pull request #1724 from MellowArpeggiation/master
Add support for all Blender f-curve functionality
This commit is contained in:
commit
7e9ee47d47
Binary file not shown.
@ -15,137 +15,169 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import com.hbm.render.anim.BusAnimationKeyframe.IType;
|
||||
import com.hbm.render.anim.BusAnimationKeyframe.EType;
|
||||
import com.hbm.render.anim.BusAnimationKeyframe.HType;
|
||||
import com.hbm.render.anim.BusAnimationSequence.Dimension;
|
||||
|
||||
public class AnimationLoader {
|
||||
|
||||
// The collada loader is great, but is not so backwards compatible and spews keyframes rather than doing interpolation
|
||||
// Yeah - more animation loading is not so great, but 3mb for a single door opening is maybe overkill on a 50mb mod
|
||||
// and even though the format supports multiple animations, no fucking animation software will actually export multiple animations,
|
||||
// (even though blender even has a fucking toggle for it, but it doesn't _do_ anything)
|
||||
// This instead just loads transformation data from a JSON file, turning it into a set of BusAnimations
|
||||
// See ntm-animator.blend for a JSON format creation script
|
||||
// The collada loader is great, but is not so backwards compatible and spews keyframes rather than doing interpolation
|
||||
// Yeah - more animation loading is not so great, but 3mb for a single door opening is maybe overkill on a 50mb mod
|
||||
// and even though the format supports multiple animations, no fucking animation software will actually export multiple animations,
|
||||
// (even though blender even has a fucking toggle for it, but it doesn't _do_ anything)
|
||||
// This instead just loads transformation data from a JSON file, turning it into a set of BusAnimations
|
||||
// See ntm-animator.blend for a JSON format creation script
|
||||
|
||||
// "How do I make animations?"
|
||||
// See ntm-animator.blend, it has the Colt/Python already setup and animated as an example, it'll generate JSON data that this can load
|
||||
// "How do I make animations?"
|
||||
// See ntm-animator.blend, it has the Colt/Python already setup and animated as an example, it'll generate JSON data that this can load
|
||||
|
||||
public static final Gson gson = new Gson();
|
||||
|
||||
|
||||
public static HashMap<String, BusAnimation> load(ResourceLocation file) {
|
||||
HashMap<String, BusAnimation> animations = new HashMap<String, BusAnimation>();
|
||||
public static HashMap<String, BusAnimation> load(ResourceLocation file) {
|
||||
HashMap<String, BusAnimation> animations = new HashMap<String, BusAnimation>();
|
||||
|
||||
InputStream in;
|
||||
try {
|
||||
in = Minecraft.getMinecraft().getResourceManager().getResource(file).getInputStream();
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
InputStream in;
|
||||
try {
|
||||
in = Minecraft.getMinecraft().getResourceManager().getResource(file).getInputStream();
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStreamReader reader = new InputStreamReader(in);
|
||||
JsonObject json = gson.fromJson(reader, JsonObject.class);
|
||||
InputStreamReader reader = new InputStreamReader(in);
|
||||
JsonObject json = gson.fromJson(reader, JsonObject.class);
|
||||
|
||||
// Load our model offsets, we'll place these into all the sequences that share the name of the offset
|
||||
// The offsets are only required when sequences are played for an object, which is why we don't globally offset! The obj rendering handles the non-animated case fine
|
||||
// Effectively, this removes double translation AND ensures that rotations occur around the individual object origin, rather than the weapon origin
|
||||
HashMap<String, double[]> offsets = new HashMap<String, double[]>();
|
||||
for (Map.Entry<String, JsonElement> root : json.getAsJsonObject("offset").entrySet()) {
|
||||
double[] offset = new double[3];
|
||||
// Load our model offsets, we'll place these into all the sequences that share the name of the offset
|
||||
// The offsets are only required when sequences are played for an object, which is why we don't globally offset! The obj rendering handles the non-animated case fine
|
||||
// Effectively, this removes double translation AND ensures that rotations occur around the individual object origin, rather than the weapon origin
|
||||
HashMap<String, double[]> offsets = new HashMap<String, double[]>();
|
||||
for(Map.Entry<String, JsonElement> root : json.getAsJsonObject("offset").entrySet()) {
|
||||
double[] offset = new double[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
offset[i] = root.getValue().getAsJsonArray().get(i).getAsDouble();
|
||||
}
|
||||
for(int i = 0; i < 3; i++) {
|
||||
offset[i] = root.getValue().getAsJsonArray().get(i).getAsDouble();
|
||||
}
|
||||
|
||||
offsets.put(root.getKey(), offset);
|
||||
}
|
||||
offsets.put(root.getKey(), offset);
|
||||
}
|
||||
|
||||
|
||||
// Top level parsing, this is for the animation name as set in Blender
|
||||
for (Map.Entry<String, JsonElement> root : json.getAsJsonObject("anim").entrySet()) {
|
||||
BusAnimation animation = new BusAnimation();
|
||||
// Top level parsing, this is for the animation name as set in Blender
|
||||
for(Map.Entry<String, JsonElement> root : json.getAsJsonObject("anim").entrySet()) {
|
||||
BusAnimation animation = new BusAnimation();
|
||||
|
||||
// Loading the buses for this animation
|
||||
JsonObject entryObject = root.getValue().getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> model : entryObject.entrySet()) {
|
||||
String modelName = model.getKey();
|
||||
double[] offset = new double[3];
|
||||
if (offsets.containsKey(modelName)) offset = offsets.get(modelName);
|
||||
animation.addBus(modelName, loadSequence(model.getValue().getAsJsonObject(), offset));
|
||||
}
|
||||
// Loading the buses for this animation
|
||||
JsonObject entryObject = root.getValue().getAsJsonObject();
|
||||
for(Map.Entry<String, JsonElement> model : entryObject.entrySet()) {
|
||||
String modelName = model.getKey();
|
||||
double[] offset = new double[3];
|
||||
if (offsets.containsKey(modelName)) offset = offsets.get(modelName);
|
||||
animation.addBus(modelName, loadSequence(model.getValue().getAsJsonObject(), offset));
|
||||
}
|
||||
|
||||
animations.put(root.getKey(), animation);
|
||||
}
|
||||
animations.put(root.getKey(), animation);
|
||||
}
|
||||
|
||||
return animations;
|
||||
}
|
||||
return animations;
|
||||
}
|
||||
|
||||
private static BusAnimationSequence loadSequence(JsonObject json, double[] offset) {
|
||||
BusAnimationSequence sequence = new BusAnimationSequence();
|
||||
private static BusAnimationSequence loadSequence(JsonObject json, double[] offset) {
|
||||
BusAnimationSequence sequence = new BusAnimationSequence();
|
||||
|
||||
// Location fcurves
|
||||
if (json.has("location")) {
|
||||
JsonObject location = json.getAsJsonObject("location");
|
||||
// Location fcurves
|
||||
if(json.has("location")) {
|
||||
JsonObject location = json.getAsJsonObject("location");
|
||||
|
||||
if (location.has("x")) {
|
||||
addToSequence(sequence, Dimension.TX, location.getAsJsonArray("x"));
|
||||
}
|
||||
if (location.has("y")) {
|
||||
addToSequence(sequence, Dimension.TY, location.getAsJsonArray("y"));
|
||||
}
|
||||
if (location.has("z")) {
|
||||
addToSequence(sequence, Dimension.TZ, location.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
if(location.has("x")) {
|
||||
addToSequence(sequence, Dimension.TX, location.getAsJsonArray("x"));
|
||||
}
|
||||
if(location.has("y")) {
|
||||
addToSequence(sequence, Dimension.TY, location.getAsJsonArray("y"));
|
||||
}
|
||||
if(location.has("z")) {
|
||||
addToSequence(sequence, Dimension.TZ, location.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
|
||||
// Rotation fcurves, only euler at the moment
|
||||
if (json.has("rotation_euler")) {
|
||||
JsonObject rotation = json.getAsJsonObject("rotation_euler");
|
||||
// Rotation fcurves, only euler at the moment
|
||||
if(json.has("rotation_euler")) {
|
||||
JsonObject rotation = json.getAsJsonObject("rotation_euler");
|
||||
|
||||
if (rotation.has("x")) {
|
||||
addToSequence(sequence, Dimension.RX, rotation.getAsJsonArray("x"));
|
||||
}
|
||||
if (rotation.has("y")) {
|
||||
addToSequence(sequence, Dimension.RY, rotation.getAsJsonArray("y"));
|
||||
}
|
||||
if (rotation.has("z")) {
|
||||
addToSequence(sequence, Dimension.RZ, rotation.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
if(rotation.has("x")) {
|
||||
addToSequence(sequence, Dimension.RX, rotation.getAsJsonArray("x"));
|
||||
}
|
||||
if(rotation.has("y")) {
|
||||
addToSequence(sequence, Dimension.RY, rotation.getAsJsonArray("y"));
|
||||
}
|
||||
if(rotation.has("z")) {
|
||||
addToSequence(sequence, Dimension.RZ, rotation.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
|
||||
// Scale fcurves
|
||||
if (json.has("scale")) {
|
||||
JsonObject scale = json.getAsJsonObject("scale");
|
||||
// Scale fcurves
|
||||
if(json.has("scale")) {
|
||||
JsonObject scale = json.getAsJsonObject("scale");
|
||||
|
||||
if (scale.has("x")) {
|
||||
addToSequence(sequence, Dimension.SX, scale.getAsJsonArray("x"));
|
||||
}
|
||||
if (scale.has("y")) {
|
||||
addToSequence(sequence, Dimension.SY, scale.getAsJsonArray("y"));
|
||||
}
|
||||
if (scale.has("z")) {
|
||||
addToSequence(sequence, Dimension.SZ, scale.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
if(scale.has("x")) {
|
||||
addToSequence(sequence, Dimension.SX, scale.getAsJsonArray("x"));
|
||||
}
|
||||
if(scale.has("y")) {
|
||||
addToSequence(sequence, Dimension.SY, scale.getAsJsonArray("y"));
|
||||
}
|
||||
if(scale.has("z")) {
|
||||
addToSequence(sequence, Dimension.SZ, scale.getAsJsonArray("z"));
|
||||
}
|
||||
}
|
||||
|
||||
sequence.offset = offset;
|
||||
sequence.offset = offset;
|
||||
|
||||
return sequence;
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
private static void addToSequence(BusAnimationSequence sequence, Dimension dimension, JsonArray array) {
|
||||
for (JsonElement element : array) {
|
||||
sequence.addKeyframe(dimension, loadKeyframe(element));
|
||||
}
|
||||
}
|
||||
private static void addToSequence(BusAnimationSequence sequence, Dimension dimension, JsonArray array) {
|
||||
IType prevInterp = null;
|
||||
for(JsonElement element : array) {
|
||||
BusAnimationKeyframe keyframe = loadKeyframe(element, prevInterp);
|
||||
prevInterp = keyframe.interpolationType;
|
||||
sequence.addKeyframe(dimension, keyframe);
|
||||
}
|
||||
}
|
||||
|
||||
private static BusAnimationKeyframe loadKeyframe(JsonElement element) {
|
||||
JsonArray array = element.getAsJsonArray();
|
||||
private static BusAnimationKeyframe loadKeyframe(JsonElement element, IType prevInterp) {
|
||||
JsonArray array = element.getAsJsonArray();
|
||||
|
||||
double value = array.get(0).getAsDouble();
|
||||
int duration = array.get(1).getAsInt();
|
||||
IType interpolation = array.size() >= 3 ? IType.valueOf(array.get(2).getAsString()) : IType.LINEAR;
|
||||
double value = array.get(0).getAsDouble();
|
||||
int duration = array.get(1).getAsInt();
|
||||
IType interpolation = array.size() >= 3 ? IType.valueOf(array.get(2).getAsString()) : IType.LINEAR;
|
||||
EType easing = array.size() >= 4 ? EType.valueOf(array.get(3).getAsString()) : EType.AUTO;
|
||||
|
||||
return new BusAnimationKeyframe(value, duration, interpolation);
|
||||
}
|
||||
BusAnimationKeyframe keyframe = new BusAnimationKeyframe(value, duration, interpolation, easing);
|
||||
|
||||
int i = 4;
|
||||
|
||||
if(prevInterp == IType.BEZIER) {
|
||||
keyframe.leftX = array.get(i++).getAsDouble();
|
||||
keyframe.leftY = array.get(i++).getAsDouble();
|
||||
keyframe.leftType = HType.valueOf(array.get(i++).getAsString());
|
||||
}
|
||||
|
||||
if(interpolation == IType.LINEAR || interpolation == IType.CONSTANT)
|
||||
return keyframe;
|
||||
|
||||
if(interpolation == IType.BEZIER) {
|
||||
keyframe.rightX = array.get(i++).getAsDouble();
|
||||
keyframe.rightY = array.get(i++).getAsDouble();
|
||||
keyframe.rightType = HType.valueOf(array.get(i++).getAsString());
|
||||
}
|
||||
|
||||
if(interpolation == IType.ELASTIC) {
|
||||
keyframe.amplitude = array.get(i++).getAsDouble();
|
||||
keyframe.period = array.get(i++).getAsDouble();
|
||||
} else if(interpolation == IType.BACK) {
|
||||
keyframe.back = array.get(i++).getAsDouble();
|
||||
}
|
||||
|
||||
return keyframe;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,10 +2,10 @@ package com.hbm.render.anim;
|
||||
|
||||
//"pieces" that make up a bus
|
||||
public class BusAnimationKeyframe {
|
||||
|
||||
//whether the next frame "snaps" to the intended value or has interpolation
|
||||
//it's an enum so stuff like accelerated animations between just
|
||||
//two frames could be implemented
|
||||
|
||||
// whether the next frame "snaps" to the intended value or has interpolation
|
||||
// it's an enum so stuff like accelerated animations between just
|
||||
// two frames could be implemented
|
||||
public static enum IType {
|
||||
/** Teleport */
|
||||
CONSTANT,
|
||||
@ -17,17 +17,68 @@ public class BusAnimationKeyframe {
|
||||
SIN_DOWN,
|
||||
/** "Sine wave", first half of a sine peak, accelerating up and then decelerating, makes for smooth movement */
|
||||
SIN_FULL,
|
||||
|
||||
// blender magic curves
|
||||
BEZIER,
|
||||
|
||||
// blender inertial
|
||||
SINE,
|
||||
QUAD,
|
||||
CUBIC,
|
||||
QUART,
|
||||
QUINT,
|
||||
EXPO,
|
||||
CIRC,
|
||||
|
||||
// blendor dynamic
|
||||
BOUNCE,
|
||||
ELASTIC,
|
||||
BACK,
|
||||
}
|
||||
|
||||
// Easing
|
||||
public static enum EType {
|
||||
AUTO,
|
||||
EASE_IN,
|
||||
EASE_OUT,
|
||||
EASE_IN_OUT,
|
||||
}
|
||||
|
||||
// Handle type
|
||||
public static enum HType {
|
||||
FREE,
|
||||
ALIGNED,
|
||||
VECTOR,
|
||||
AUTO,
|
||||
AUTO_CLAMPED,
|
||||
}
|
||||
|
||||
public double value;
|
||||
public IType interpolationType;
|
||||
public EType easingType;
|
||||
public int duration;
|
||||
|
||||
//this one can be used for "reset" type keyframes
|
||||
|
||||
// bezier handles
|
||||
public double leftX;
|
||||
public double leftY;
|
||||
public HType leftType;
|
||||
public double rightX;
|
||||
public double rightY;
|
||||
public HType rightType;
|
||||
|
||||
// elastics
|
||||
public double amplitude;
|
||||
public double period;
|
||||
|
||||
// back (overshoot)
|
||||
public double back;
|
||||
|
||||
// this one can be used for "reset" type keyframes
|
||||
public BusAnimationKeyframe() {
|
||||
this.value = 0;
|
||||
this.duration = 1;
|
||||
this.interpolationType = IType.LINEAR;
|
||||
this.easingType = EType.AUTO;
|
||||
}
|
||||
|
||||
public BusAnimationKeyframe(double value, int duration) {
|
||||
@ -41,4 +92,488 @@ public class BusAnimationKeyframe {
|
||||
this.interpolationType = interpolation;
|
||||
}
|
||||
|
||||
public BusAnimationKeyframe(double value, int duration, IType interpolation, EType easing) {
|
||||
this(value, duration, interpolation);
|
||||
this.easingType = easing;
|
||||
}
|
||||
|
||||
public double interpolate(double startTime, double currentTime, BusAnimationKeyframe previous) {
|
||||
if(previous == null)
|
||||
previous = new BusAnimationKeyframe();
|
||||
|
||||
double a = value;
|
||||
double b = previous.value;
|
||||
double t = time(startTime, currentTime, duration);
|
||||
|
||||
double begin = previous.value;
|
||||
double change = value - previous.value;
|
||||
double time = currentTime - startTime;
|
||||
|
||||
if(previous.interpolationType == IType.BEZIER) {
|
||||
double v1x = startTime;
|
||||
double v1y = previous.value;
|
||||
double v2x = previous.rightX;
|
||||
double v2y = previous.rightY;
|
||||
|
||||
double v3x = leftX;
|
||||
double v3y = leftY;
|
||||
double v4x = startTime + duration;
|
||||
double v4y = value;
|
||||
|
||||
double curveT = findZero(currentTime, v1x, v2x, v3x, v4x);
|
||||
return cubicBezier(v1y, v2y, v3y, v4y, curveT);
|
||||
} else if(previous.interpolationType == IType.BACK) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_IN: return BLI_easing_back_ease_in(time, begin, change, duration, previous.back);
|
||||
case EASE_IN_OUT: return BLI_easing_back_ease_in_out(time, begin, change, duration, previous.back);
|
||||
default: return BLI_easing_back_ease_out(time, begin, change, duration, previous.back);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.BOUNCE) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_IN: return BLI_easing_bounce_ease_in(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_bounce_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_bounce_ease_out(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.CIRC) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_circ_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_circ_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_circ_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.CUBIC) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_cubic_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_cubic_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_cubic_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.ELASTIC) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_IN: return BLI_easing_elastic_ease_in(time, begin, change, duration, previous.amplitude, previous.period);
|
||||
case EASE_IN_OUT: return BLI_easing_elastic_ease_in_out(time, begin, change, duration, previous.amplitude, previous.period);
|
||||
default: return BLI_easing_elastic_ease_out(time, begin, change, duration, previous.amplitude, previous.period);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.EXPO) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_expo_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_expo_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_expo_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.QUAD) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_quad_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_quad_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_quad_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.QUART) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_quart_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_quart_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_quart_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.QUINT) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_quint_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_quint_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_quint_ease_in(time, begin, change, duration);
|
||||
}
|
||||
} else if(previous.interpolationType == IType.SINE) {
|
||||
switch (previous.easingType) {
|
||||
case EASE_OUT: return BLI_easing_sine_ease_out(time, begin, change, duration);
|
||||
case EASE_IN_OUT: return BLI_easing_sine_ease_in_out(time, begin, change, duration);
|
||||
default: return BLI_easing_sine_ease_in(time, begin, change, duration);
|
||||
}
|
||||
}
|
||||
|
||||
return (a - b) * t + b;
|
||||
}
|
||||
|
||||
private double sqrt3(double d) {
|
||||
return Math.exp(Math.log(d) / 3.0);
|
||||
}
|
||||
|
||||
private double time(double start, double end, double duration) {
|
||||
if(interpolationType == IType.SIN_UP) return -Math.sin(((end - start) / duration * Math.PI + Math.PI) / 2) + 1;
|
||||
if(interpolationType == IType.SIN_DOWN) return Math.sin((end - start) / duration * Math.PI / 2);
|
||||
if(interpolationType == IType.SIN_FULL) return (-Math.cos((end - start) / duration * Math.PI) + 1) / 2D;
|
||||
return (end - start) / duration;
|
||||
}
|
||||
|
||||
// Blender bezier solvers, but rewritten (pain)
|
||||
private double solveCubic(double c0, double c1, double c2, double c3) {
|
||||
if(c3 != 0) {
|
||||
double a = c2 / c3;
|
||||
double b = c1 / c3;
|
||||
double c = c0 / c3;
|
||||
a = a / 3;
|
||||
|
||||
double p = b / 3 - a * a;
|
||||
double q = (2 * a * a * a - a * b + c) / 2;
|
||||
double d = q * q + p * p * p;
|
||||
|
||||
if(d > 0) {
|
||||
double t = Math.sqrt(d);
|
||||
return sqrt3(-q + t) + sqrt3(-q - t) - a;
|
||||
}
|
||||
|
||||
if(d == 0) {
|
||||
double t = sqrt3(-q);
|
||||
double result = 2 * t - a;
|
||||
if(result < 0.000001 || result > 1.000001) {
|
||||
result = -t - a;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double phi = Math.acos(-q / Math.sqrt(-(p * p * p)));
|
||||
double t = Math.sqrt(-p);
|
||||
p = Math.cos(phi / 3);
|
||||
q = Math.sqrt(3 - 3 * p * p);
|
||||
double result = 2 * t * p - a;
|
||||
if(result < 0.000001 || result > 1.000001) {
|
||||
result = -t * (p + q) - a;
|
||||
}
|
||||
if(result < 0.000001 || result > 1.000001) {
|
||||
result = -t * (p - q) - a;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double a = c2;
|
||||
double b = c1;
|
||||
double c = c0;
|
||||
|
||||
if(a != 0) {
|
||||
double p = b * b - 4 * a * c;
|
||||
|
||||
if(p > 0) {
|
||||
p = Math.sqrt(p);
|
||||
double result = (-b - p) / (2 * a);
|
||||
if(result < 0.000001 || result > 1.000001) {
|
||||
result = (-b + p) / (2 * a);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if(p == 0) {
|
||||
return -b / (2 * a);
|
||||
}
|
||||
}
|
||||
|
||||
if(b != 0) {
|
||||
return -c / b;
|
||||
}
|
||||
|
||||
if(c == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double findZero(double t, double x1, double x2, double x3, double x4) {
|
||||
double c0 = x1 - t;
|
||||
double c1 = 3.0f * (x2 - x1);
|
||||
double c2 = 3.0f * (x1 - 2.0f * x2 + x3);
|
||||
double c3 = x4 - x1 + 3.0f * (x2 - x3);
|
||||
|
||||
return solveCubic(c0, c1, c2, c3);
|
||||
}
|
||||
|
||||
private double cubicBezier(double y1, double y2, double y3, double y4, double t) {
|
||||
double c0 = y1;
|
||||
double c1 = 3.0f * (y2 - y1);
|
||||
double c2 = 3.0f * (y1 - 2.0f * y2 + y3);
|
||||
double c3 = y4 - y1 + 3.0f * (y2 - y3);
|
||||
|
||||
return c0 + t * c1 + t * t * c2 + t * t * t * c3;
|
||||
}
|
||||
|
||||
/**
|
||||
* EASING FUNCTIONS, taken directly from Blender `easing.c`
|
||||
*/
|
||||
|
||||
double BLI_easing_back_ease_in(double time, double begin, double change, double duration, double overshoot) {
|
||||
time /= duration;
|
||||
return change * time * time * ((overshoot + 1) * time - overshoot) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_back_ease_out(double time, double begin, double change, double duration, double overshoot) {
|
||||
time = time / duration - 1;
|
||||
return change * (time * time * ((overshoot + 1) * time + overshoot) + 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_back_ease_in_out(double time, double begin, double change, double duration, double overshoot) {
|
||||
overshoot *= 1.525f;
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return change / 2 * (time * time * ((overshoot + 1) * time - overshoot)) + begin;
|
||||
}
|
||||
time -= 2.0f;
|
||||
return change / 2 * (time * time * ((overshoot + 1) * time + overshoot) + 2) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_bounce_ease_out(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
if(time < (1 / 2.75f)) {
|
||||
return change * (7.5625f * time * time) + begin;
|
||||
}
|
||||
if(time < (2 / 2.75f)) {
|
||||
time -= (1.5f / 2.75f);
|
||||
return change * ((7.5625f * time) * time + 0.75f) + begin;
|
||||
}
|
||||
if(time < (2.5f / 2.75f)) {
|
||||
time -= (2.25f / 2.75f);
|
||||
return change * ((7.5625f * time) * time + 0.9375f) + begin;
|
||||
}
|
||||
time -= (2.625f / 2.75f);
|
||||
return change * ((7.5625f * time) * time + 0.984375f) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_bounce_ease_in(double time, double begin, double change, double duration) {
|
||||
return change - BLI_easing_bounce_ease_out(duration - time, 0, change, duration) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_bounce_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if(time < duration / 2) {
|
||||
return BLI_easing_bounce_ease_in(time * 2, 0, change, duration) * 0.5f + begin;
|
||||
}
|
||||
return BLI_easing_bounce_ease_out(time * 2 - duration, 0, change, duration) * 0.5f + change * 0.5f + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_circ_ease_in(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return -change * (Math.sqrt(1 - time * time) - 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_circ_ease_out(double time, double begin, double change, double duration) {
|
||||
time = time / duration - 1;
|
||||
return change * Math.sqrt(1 - time * time) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_circ_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return -change / 2 * (Math.sqrt(1 - time * time) - 1) + begin;
|
||||
}
|
||||
time -= 2.0f;
|
||||
return change / 2 * (Math.sqrt(1 - time * time) + 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_cubic_ease_in(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return change * time * time * time + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_cubic_ease_out(double time, double begin, double change, double duration) {
|
||||
time = time / duration - 1;
|
||||
return change * (time * time * time + 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_cubic_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return change / 2 * time * time * time + begin;
|
||||
}
|
||||
time -= 2.0f;
|
||||
return change / 2 * (time * time * time + 2) + begin;
|
||||
}
|
||||
|
||||
double elastic_blend(double time, double change, double duration, double amplitude, double s, double f) {
|
||||
if(change != 0) {
|
||||
/*
|
||||
* Looks like a magic number,
|
||||
* but this is a part of the sine curve we need to blend from
|
||||
*/
|
||||
double t = Math.abs(s);
|
||||
if(amplitude != 0) {
|
||||
f *= amplitude / Math.abs(change);
|
||||
} else {
|
||||
f = 0.0f;
|
||||
}
|
||||
|
||||
if(Math.abs(time * duration) < t) {
|
||||
double l = Math.abs(time * duration) / t;
|
||||
f = (f * l) + (1.0f - l);
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
double BLI_easing_elastic_ease_in(double time, double begin, double change, double duration, double amplitude, double period) {
|
||||
double s;
|
||||
double f = 1.0f;
|
||||
|
||||
if(time == 0.0f) {
|
||||
return begin;
|
||||
}
|
||||
|
||||
if((time /= duration) == 1.0f) {
|
||||
return begin + change;
|
||||
}
|
||||
time -= 1.0f;
|
||||
if(period == 0) {
|
||||
period = duration * 0.3f;
|
||||
}
|
||||
if(amplitude == 0 || amplitude < Math.abs(change)) {
|
||||
s = period / 4;
|
||||
f = elastic_blend(time, change, duration, amplitude, s, f);
|
||||
amplitude = change;
|
||||
} else {
|
||||
s = period / (2 * (double) Math.PI) * Math.asin(change / amplitude);
|
||||
}
|
||||
|
||||
return (-f * (amplitude * Math.pow(2, 10 * time) * Math.sin((time * duration - s) * (2 * (double) Math.PI) / period))) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_elastic_ease_out(double time, double begin, double change, double duration, double amplitude, double period) {
|
||||
double s;
|
||||
double f = 1.0f;
|
||||
|
||||
if(time == 0.0f) {
|
||||
return begin;
|
||||
}
|
||||
if((time /= duration) == 1.0f) {
|
||||
return begin + change;
|
||||
}
|
||||
time = -time;
|
||||
if(period == 0) {
|
||||
period = duration * 0.3f;
|
||||
}
|
||||
if(amplitude == 0 || amplitude < Math.abs(change)) {
|
||||
s = period / 4;
|
||||
f = elastic_blend(time, change, duration, amplitude, s, f);
|
||||
amplitude = change;
|
||||
} else {
|
||||
s = period / (2 * (double) Math.PI) * Math.asin(change / amplitude);
|
||||
}
|
||||
|
||||
return (f * (amplitude * Math.pow(2, 10 * time) * Math.sin((time * duration - s) * (2 * (double) Math.PI) / period))) + change + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_elastic_ease_in_out(double time, double begin, double change, double duration, double amplitude, double period) {
|
||||
double s;
|
||||
double f = 1.0f;
|
||||
|
||||
if(time == 0.0f) {
|
||||
return begin;
|
||||
}
|
||||
if((time /= duration / 2) == 2.0f) {
|
||||
return begin + change;
|
||||
}
|
||||
time -= 1.0f;
|
||||
if(period == 0) {
|
||||
period = duration * (0.3f * 1.5f);
|
||||
}
|
||||
if(amplitude == 0 || amplitude < Math.abs(change)) {
|
||||
s = period / 4;
|
||||
f = elastic_blend(time, change, duration, amplitude, s, f);
|
||||
amplitude = change;
|
||||
} else {
|
||||
s = period / (2 * (double) Math.PI) * Math.asin(change / amplitude);
|
||||
}
|
||||
|
||||
if(time < 0.0f) {
|
||||
f *= -0.5f;
|
||||
return (f * (amplitude * Math.pow(2, 10 * time) * Math.sin((time * duration - s) * (2 * (double) Math.PI) / period))) + begin;
|
||||
}
|
||||
|
||||
time = -time;
|
||||
f *= 0.5f;
|
||||
return (f * (amplitude * Math.pow(2, 10 * time) * Math.sin((time * duration - s) * (2 * (double) Math.PI) / period))) + change + begin;
|
||||
}
|
||||
|
||||
static final double pow_min = 0.0009765625f; /* = 2^(-10) */
|
||||
static final double pow_scale = 1.0f / (1.0f - 0.0009765625f);
|
||||
|
||||
double BLI_easing_expo_ease_in(double time, double begin, double change, double duration) {
|
||||
if(time == 0.0) {
|
||||
return begin;
|
||||
}
|
||||
return change * (Math.pow(2, 10 * (time / duration - 1)) - pow_min) * pow_scale + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_expo_ease_out(double time, double begin, double change, double duration) {
|
||||
if(time == 0.0) {
|
||||
return begin;
|
||||
}
|
||||
return change * (1 - (Math.pow(2, -10 * time / duration) - pow_min) * pow_scale) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_expo_ease_in_out(double time, double begin, double change, double duration) {
|
||||
double duration_half = duration / 2.0f;
|
||||
double change_half = change / 2.0f;
|
||||
if(time <= duration_half) {
|
||||
return BLI_easing_expo_ease_in(time, begin, change_half, duration_half);
|
||||
}
|
||||
return BLI_easing_expo_ease_out(time - duration_half, begin + change_half, change_half, duration_half);
|
||||
}
|
||||
|
||||
double BLI_easing_linear_ease(double time, double begin, double change, double duration) {
|
||||
return change * time / duration + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quad_ease_in(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return change * time * time + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quad_ease_out(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return -change * time * (time - 2) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quad_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return change / 2 * time * time + begin;
|
||||
}
|
||||
time -= 1.0f;
|
||||
return -change / 2 * (time * (time - 2) - 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quart_ease_in(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return change * time * time * time * time + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quart_ease_out(double time, double begin, double change, double duration) {
|
||||
time = time / duration - 1;
|
||||
return -change * (time * time * time * time - 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quart_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return change / 2 * time * time * time * time + begin;
|
||||
}
|
||||
time -= 2.0f;
|
||||
return -change / 2 * (time * time * time * time - 2) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quint_ease_in(double time, double begin, double change, double duration) {
|
||||
time /= duration;
|
||||
return change * time * time * time * time * time + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quint_ease_out(double time, double begin, double change, double duration) {
|
||||
time = time / duration - 1;
|
||||
return change * (time * time * time * time * time + 1) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_quint_ease_in_out(double time, double begin, double change, double duration) {
|
||||
if((time /= duration / 2) < 1.0f) {
|
||||
return change / 2 * time * time * time * time * time + begin;
|
||||
}
|
||||
time -= 2.0f;
|
||||
return change / 2 * (time * time * time * time * time + 2) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_sine_ease_in(double time, double begin, double change, double duration) {
|
||||
return -change * Math.cos(time / duration * (double) Math.PI * 2) + change + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_sine_ease_out(double time, double begin, double change, double duration) {
|
||||
return change * Math.sin(time / duration * (double) Math.PI * 2) + begin;
|
||||
}
|
||||
|
||||
double BLI_easing_sine_ease_in_out(double time, double begin, double change, double duration) {
|
||||
return -change / 2 * (Math.cos((double) Math.PI * time / duration) - 1) + begin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ public class BusAnimationSequence {
|
||||
|
||||
public BusAnimationSequence() {
|
||||
// Initialise our keyframe storage, since it's multidimensional
|
||||
for (int i = 0; i < 9; i++) {
|
||||
for(int i = 0; i < 9; i++) {
|
||||
transformKeyframes.add(new ArrayList<BusAnimationKeyframe>());
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,7 @@ public class BusAnimationSequence {
|
||||
public double[] getTransformation(int millis) {
|
||||
double[] transform = new double[12];
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
for(int i = 0; i < 9; i++) {
|
||||
List<BusAnimationKeyframe> keyframes = transformKeyframes.get(i);
|
||||
|
||||
BusAnimationKeyframe currentFrame = null;
|
||||
@ -102,11 +102,7 @@ public class BusAnimationSequence {
|
||||
continue;
|
||||
}
|
||||
|
||||
double a = currentFrame.value;
|
||||
double b = previousFrame != null ? previousFrame.value : 0;
|
||||
double t = interpolate(startTime, millis, currentFrame.duration, currentFrame.interpolationType);
|
||||
|
||||
transform[i] = (a - b) * t + b;
|
||||
transform[i] = currentFrame.interpolate(startTime, millis, previousFrame);
|
||||
}
|
||||
|
||||
transform[9] = offset[0];
|
||||
@ -116,20 +112,12 @@ public class BusAnimationSequence {
|
||||
return transform;
|
||||
}
|
||||
|
||||
public double interpolate(double start, double end, double duration, IType interp) {
|
||||
if(interp == IType.LINEAR) return (end - start) / duration;
|
||||
if(interp == IType.SIN_UP) return -Math.sin(((end - start) / duration * Math.PI + Math.PI) / 2) + 1;
|
||||
if(interp == IType.SIN_DOWN) return Math.sin((end - start) / duration * Math.PI / 2);
|
||||
if(interp == IType.SIN_FULL) return (-Math.cos((end - start) / duration * Math.PI) + 1) / 2D;
|
||||
return end - start;
|
||||
}
|
||||
|
||||
public int getTotalTime() {
|
||||
int highestTime = 0;
|
||||
|
||||
for (List<BusAnimationKeyframe> keyframes: transformKeyframes) {
|
||||
for(List<BusAnimationKeyframe> keyframes : transformKeyframes) {
|
||||
int time = 0;
|
||||
for (BusAnimationKeyframe frame: keyframes) {
|
||||
for(BusAnimationKeyframe frame : keyframes) {
|
||||
time += frame.duration;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user