Included Path Interpolation methods in KeyframeList and uses it in PathPreviewRenderer

Created newInstance() method in KeyframeValue interface
This commit is contained in:
CrushedPixel
2015-07-08 23:55:48 +02:00
parent cd27ccd855
commit 16b74d9824
12 changed files with 97 additions and 77 deletions

View File

@@ -1,7 +1,6 @@
package eu.crushedpixel.replaymod.assets;
import eu.crushedpixel.replaymod.holders.*;
import eu.crushedpixel.replaymod.interpolation.GenericSplineInterpolation;
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
import eu.crushedpixel.replaymod.registry.ResourceHelper;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
@@ -17,7 +16,7 @@ import java.util.UUID;
public class CustomImageObject implements GuiEntryListEntry {
public CustomImageObject(Transformations transformations, String name, UUID assetUUID) throws IOException {
public CustomImageObject(String name, UUID assetUUID) throws IOException {
this.name = name;
setLinkedAsset(assetUUID);
@@ -90,13 +89,9 @@ public class CustomImageObject implements GuiEntryListEntry {
* Keyframing Code
*/
private KeyframeList<Keyframe<Position>> anchorPointKeyframes, positionKeyframes, orientationKeyframes;
private KeyframeList<Keyframe<Point>> scaleKeyframes;
private KeyframeList<Keyframe<TimestampValue>> opacityKeyframes;
private GenericSplineInterpolation<Position> anchorSpline, positionSpline, orientationSpline;
private GenericSplineInterpolation<Point> scaleSpline;
private GenericSplineInterpolation<TimestampValue> opacitySpline;
private KeyframeList<Position> anchorPointKeyframes, positionKeyframes, orientationKeyframes;
private KeyframeList<Point> scaleKeyframes;
private KeyframeList<TimestampValue> opacityKeyframes;
public Transformations getTransformationsForTimestamp(int timestamp) {
return null; //TODO

View File

@@ -1,6 +1,5 @@
package eu.crushedpixel.replaymod.events;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.Position;
import eu.crushedpixel.replaymod.holders.TimestampValue;
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
@@ -14,7 +13,7 @@ import net.minecraftforge.fml.common.eventhandler.Event;
@EqualsAndHashCode(callSuper=true)
public class KeyframesModifyEvent extends Event {
private KeyframeList<Keyframe<Position>> positionKeyframes;
private KeyframeList<Keyframe<TimestampValue>> timeKeyframes;
private KeyframeList<Position> positionKeyframes;
private KeyframeList<TimestampValue> timeKeyframes;
}

View File

@@ -59,8 +59,8 @@ public class GuiEditKeyframe extends GuiScreen {
ReplayHandler.selectKeyframe(null);
KeyframeList<Keyframe<Position>> positionKeyframes = ReplayHandler.getPositionKeyframes();
KeyframeList<Keyframe<TimestampValue>> timeKeyframes = ReplayHandler.getTimeKeyframes();
KeyframeList<Position> positionKeyframes = ReplayHandler.getPositionKeyframes();
KeyframeList<TimestampValue> timeKeyframes = ReplayHandler.getTimeKeyframes();
if(posKeyframe) {
previous = positionKeyframes.getPreviousKeyframe(keyframe.getRealTimestamp() - 1);

View File

@@ -13,7 +13,7 @@ public class Keyframe<T extends KeyframeValue> {
private int realTimestamp;
private T value;
public Keyframe copy() {
public Keyframe<T> copy() {
return new Keyframe<T>(realTimestamp, value);
}
}

View File

@@ -7,9 +7,13 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class Point extends KeyframeValue {
public class Point implements KeyframeValue {
@Interpolate
public double x, y;
@Override
public Point newInstance() {
return new Point();
}
}

View File

@@ -4,7 +4,6 @@ import eu.crushedpixel.replaymod.interpolation.Interpolate;
import eu.crushedpixel.replaymod.interpolation.KeyframeValue;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
@@ -12,8 +11,7 @@ import net.minecraft.entity.Entity;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper=true)
public class Position extends KeyframeValue {
public class Position implements KeyframeValue {
@Interpolate
public double x, y, z;
@@ -44,4 +42,9 @@ public class Position extends KeyframeValue {
double dz = this.z - z;
return dx * dx + dy * dy + dz * dz;
}
@Override
public Position newInstance() {
return new Position();
}
}

View File

@@ -7,9 +7,14 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class TimestampValue extends KeyframeValue {
public class TimestampValue implements KeyframeValue {
@Interpolate
public double value;
@Override
public TimestampValue newInstance() {
return new TimestampValue();
}
}

View File

@@ -7,26 +7,30 @@ import eu.crushedpixel.replaymod.holders.Position;
import java.util.ArrayList;
import java.util.List;
public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>> {
private static final KeyframeComparator KEYFRAME_COMPARATOR = new KeyframeComparator();
private Boolean previousCallLinear = null;
private Interpolation<K> interpolation;
@Override
public boolean add(T t) {
public boolean add(Keyframe<K> t) {
boolean success = super.add(t);
sort();
return success;
}
@Override
public void add(int index, T element) {
public void add(int index, Keyframe<K> element) {
super.add(index, element);
sort();
}
@Override
public T remove(int index) {
T removed = super.remove(index);
public Keyframe<K> remove(int index) {
Keyframe<K> removed = super.remove(index);
sort();
return removed;
}
@@ -39,6 +43,7 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
}
public void sort() {
previousCallLinear = null;
sort(KEYFRAME_COMPARATOR);
}
@@ -47,14 +52,14 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
* @param realTime The value to use
* @return The first Keyframe prior to the given value
*/
public T getPreviousKeyframe(int realTime) {
public Keyframe<K> getPreviousKeyframe(int realTime) {
if(this.isEmpty()) return null;
T backup = null;
Keyframe<K> backup = null;
List<T> found = new ArrayList<T>();
List<Keyframe<K>> found = new ArrayList<Keyframe<K>>();
for(T kf : this) {
for(Keyframe<K> kf : this) {
if(kf.getRealTimestamp() < realTime) {
found.add(kf);
@@ -75,12 +80,12 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
* @param realTime The value to use
* @return The first Keyframe after the given value
*/
public T getNextKeyframe(int realTime) {
public Keyframe<K> getNextKeyframe(int realTime) {
if(this.isEmpty()) return null;
T backup = null;
Keyframe<K> backup = null;
for(T kf : this) {
for(Keyframe<K> kf : this) {
if(kf.getRealTimestamp() > realTime) {
return kf; //first found element is next
@@ -99,18 +104,18 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
* @param tolerance The threshold to allow for close Keyframes
* @return The closest Keyframe, or null if no Keyframe within treshold
*/
public T getClosestKeyframeForTimestamp(int realTime, int tolerance) {
List<T> found = new ArrayList<T>();
for(T kf : this) {
public Keyframe<K> getClosestKeyframeForTimestamp(int realTime, int tolerance) {
List<Keyframe<K>> found = new ArrayList<Keyframe<K>>();
for(Keyframe<K> kf : this) {
if(!(kf.getValue() instanceof Position)) continue;
if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) {
found.add(kf);
}
}
T closest = null;
Keyframe<K> closest = null;
for(T kf : found) {
for(Keyframe<K> kf : found) {
if(closest == null || Math.abs(closest.getRealTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) {
closest = kf;
}
@@ -118,23 +123,45 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
return closest;
}
public T first() {
public Keyframe<K> first() {
if(isEmpty()) return null;
return get(0);
}
public T last() {
public Keyframe<K> last() {
if(isEmpty()) return null;
return get(size()-1);
}
public K getInterpolatedValueForTimestamp(int timestamp, boolean linear) {
return getInterpolatedValueForPathPosition(getPositionOnPath(timestamp), linear);
}
public K getInterpolatedValueForPathPosition(float pathPosition, boolean linear) {
K toApply = (K)first().getValue().newInstance();
if(previousCallLinear != (Boolean)linear) {
interpolation = linear ? new GenericLinearInterpolation<K>() : new GenericSplineInterpolation<K>();
for(Keyframe<K> keyframe : this) {
interpolation.addPoint(keyframe.getValue());
}
interpolation.prepare();
}
interpolation.applyPoint(pathPosition, toApply);
return toApply;
}
/**
* Returns a value between 0 and 1, representing the number that should be passed
* to Interpolation#getValue() calls on this list of Keyframes.
* @param timestamp The value to use
* @return A value between 0 and 1
*/
public float getPositionOnSpline(int timestamp) {
private float getPositionOnPath(int timestamp) {
Keyframe previousKeyframe = getPreviousKeyframe(timestamp);
Keyframe nextKeyframe = getNextKeyframe(timestamp);

View File

@@ -7,6 +7,8 @@ package eu.crushedpixel.replaymod.interpolation;
* <br><br>
* It is recommended for KeyframeValue subclasses to have a @NoArgsConstructor annotation.
*/
public abstract class KeyframeValue {
public interface KeyframeValue {
public KeyframeValue newInstance();
}

View File

@@ -5,7 +5,7 @@ import eu.crushedpixel.replaymod.events.KeyframesModifyEvent;
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.Position;
import eu.crushedpixel.replaymod.interpolation.GenericSplineInterpolation;
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
@@ -26,11 +26,9 @@ public class PathPreviewRenderer {
private static final Minecraft mc = Minecraft.getMinecraft();
private GenericSplineInterpolation<Position> spline = new GenericSplineInterpolation<Position>();
private DistanceComparator distanceComparator = new DistanceComparator();
private List<Keyframe<Position>> keyframes = new ArrayList<Keyframe<Position>>();
private KeyframeList<Position> keyframes;
@SubscribeEvent
public void renderCameraPath(RenderWorldLastEvent event) {
@@ -51,28 +49,25 @@ public class PathPreviewRenderer {
GlStateManager.disableTexture2D();
if(spline.getPoints().size() > 1) {
if(keyframes.size() > 1) {
Position prev = null;
if(ReplayMod.replaySettings.isLinearMovement()) {
for(int i = 0; i < spline.getPoints().size(); i++) {
Position point = spline.getPoints().get(i);
for(Keyframe<Position> point : keyframes) {
if(prev != null) {
drawConnection(doubleX, doubleY, doubleZ, prev, point, Color.RED.getRGB());
drawConnection(doubleX, doubleY, doubleZ, prev, point.getValue(), Color.RED.getRGB());
}
prev = point;
prev = point.getValue();
}
} else {
float max = spline.getPoints().size() * 50;
float max = keyframes.size() * 50;
for(int i = 0; i < max; i++) {
Position point = new Position();
spline.applyPoint(i / max, point);
Position point = keyframes.getInterpolatedValueForPathPosition(i/max, false);
if(prev != null) {
drawConnection(doubleX, doubleY, doubleZ, prev, point, Color.RED.getRGB());
@@ -83,12 +78,13 @@ public class PathPreviewRenderer {
}
}
distanceComparator.setPlayerPos(doubleX, doubleY + 1.4, doubleZ);
Collections.sort(keyframes, distanceComparator);
List<Keyframe<Position>> distanceSorted = new ArrayList<Keyframe<Position>>(keyframes);
Collections.sort(distanceSorted, distanceComparator);
for(Keyframe<Position> kf : keyframes) {
for(Keyframe<Position> kf : distanceSorted) {
drawPoint(doubleX, doubleY, doubleZ, kf);
}
@@ -102,18 +98,7 @@ public class PathPreviewRenderer {
@SubscribeEvent
public void recalcSpline(KeyframesModifyEvent event) {
keyframes = new ArrayList<Keyframe<Position>>();
spline = new GenericSplineInterpolation<Position>();
for(Keyframe kf : event.getPositionKeyframes()) {
Keyframe<Position> pkf = (Keyframe<Position>)kf;
Position pos = pkf.getValue();
spline.addPoint(pos);
keyframes.add(pkf);
}
if(spline.getPoints().size() > 1) {
spline.prepare();
}
keyframes = event.getPositionKeyframes();
}
private class DistanceComparator implements Comparator<Keyframe<Position>> {

View File

@@ -50,8 +50,8 @@ public class ReplayHandler {
private static boolean inPath = false;
private static CameraEntity cameraEntity;
private static KeyframeList<Keyframe<Position>> positionKeyframes = new KeyframeList<Keyframe<Position>>();
private static KeyframeList<Keyframe<TimestampValue>> timeKeyframes = new KeyframeList<Keyframe<TimestampValue>>();
private static KeyframeList<Position> positionKeyframes = new KeyframeList<Position>();
private static KeyframeList<TimestampValue> timeKeyframes = new KeyframeList<TimestampValue>();
private static boolean inReplay = false;
private static Entity currentEntity = null;
@@ -337,16 +337,16 @@ public class ReplayHandler {
return backup;
}
public static KeyframeList<Keyframe<Position>> getPositionKeyframes() {
public static KeyframeList<Position> getPositionKeyframes() {
return positionKeyframes;
}
public static KeyframeList<Keyframe<TimestampValue>> getTimeKeyframes() {
public static KeyframeList<TimestampValue> getTimeKeyframes() {
return timeKeyframes;
}
public static KeyframeList<Keyframe> getAllKeyframes() {
KeyframeList keyframeList = new KeyframeList();
public static ArrayList<Keyframe> getAllKeyframes() {
ArrayList<Keyframe> keyframeList = new ArrayList<Keyframe>();
keyframeList.addAll(positionKeyframes);
keyframeList.addAll(timeKeyframes);

View File

@@ -205,7 +205,7 @@ public class VideoRenderer {
}
private void updateCam() {
KeyframeList<Keyframe<Position>> positionKeyframes = ReplayHandler.getPositionKeyframes();
KeyframeList<Position> positionKeyframes = ReplayHandler.getPositionKeyframes();
if (ReplayHandler.getCameraEntity() == null) {
if (mc.theWorld == null) {
@@ -268,7 +268,7 @@ public class VideoRenderer {
}
private void updateTime(Timer timer, int framesDone) {
KeyframeList<Keyframe<TimestampValue>> timeKeyframes = ReplayHandler.getTimeKeyframes();
KeyframeList<TimestampValue> timeKeyframes = ReplayHandler.getTimeKeyframes();
int videoTime = framesDone * 1000 / fps;
int timeCount = timeKeyframes.size();