diff --git a/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java index 62afce63..5d4dcd87 100644 --- a/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java +++ b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java @@ -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> anchorPointKeyframes, positionKeyframes, orientationKeyframes; - private KeyframeList> scaleKeyframes; - private KeyframeList> opacityKeyframes; - - private GenericSplineInterpolation anchorSpline, positionSpline, orientationSpline; - private GenericSplineInterpolation scaleSpline; - private GenericSplineInterpolation opacitySpline; + private KeyframeList anchorPointKeyframes, positionKeyframes, orientationKeyframes; + private KeyframeList scaleKeyframes; + private KeyframeList opacityKeyframes; public Transformations getTransformationsForTimestamp(int timestamp) { return null; //TODO diff --git a/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java b/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java index 08cdd080..61eb1e22 100644 --- a/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java @@ -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> positionKeyframes; - private KeyframeList> timeKeyframes; + private KeyframeList positionKeyframes; + private KeyframeList timeKeyframes; } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java index d49ae34d..d575ba4b 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java @@ -59,8 +59,8 @@ public class GuiEditKeyframe extends GuiScreen { ReplayHandler.selectKeyframe(null); - KeyframeList> positionKeyframes = ReplayHandler.getPositionKeyframes(); - KeyframeList> timeKeyframes = ReplayHandler.getTimeKeyframes(); + KeyframeList positionKeyframes = ReplayHandler.getPositionKeyframes(); + KeyframeList timeKeyframes = ReplayHandler.getTimeKeyframes(); if(posKeyframe) { previous = positionKeyframes.getPreviousKeyframe(keyframe.getRealTimestamp() - 1); diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java index e99dc962..2f0f265f 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java @@ -13,7 +13,7 @@ public class Keyframe { private int realTimestamp; private T value; - public Keyframe copy() { + public Keyframe copy() { return new Keyframe(realTimestamp, value); } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Point.java b/src/main/java/eu/crushedpixel/replaymod/holders/Point.java index e54c47bb..7b6aa006 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Point.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Point.java @@ -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(); + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java index c6e516d9..f16494ae 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java @@ -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(); + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java b/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java index ce718f2e..4456393d 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java @@ -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(); + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java index d68e85fb..46bf2d4f 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java @@ -7,26 +7,30 @@ import eu.crushedpixel.replaymod.holders.Position; import java.util.ArrayList; import java.util.List; -public class KeyframeList extends ArrayList { +public class KeyframeList extends ArrayList> { private static final KeyframeComparator KEYFRAME_COMPARATOR = new KeyframeComparator(); + private Boolean previousCallLinear = null; + + private Interpolation interpolation; + @Override - public boolean add(T t) { + public boolean add(Keyframe t) { boolean success = super.add(t); sort(); return success; } @Override - public void add(int index, T element) { + public void add(int index, Keyframe element) { super.add(index, element); sort(); } @Override - public T remove(int index) { - T removed = super.remove(index); + public Keyframe remove(int index) { + Keyframe removed = super.remove(index); sort(); return removed; } @@ -39,6 +43,7 @@ public class KeyframeList extends ArrayList { } public void sort() { + previousCallLinear = null; sort(KEYFRAME_COMPARATOR); } @@ -47,14 +52,14 @@ public class KeyframeList extends ArrayList { * @param realTime The value to use * @return The first Keyframe prior to the given value */ - public T getPreviousKeyframe(int realTime) { + public Keyframe getPreviousKeyframe(int realTime) { if(this.isEmpty()) return null; - T backup = null; + Keyframe backup = null; - List found = new ArrayList(); + List> found = new ArrayList>(); - for(T kf : this) { + for(Keyframe kf : this) { if(kf.getRealTimestamp() < realTime) { found.add(kf); @@ -75,12 +80,12 @@ public class KeyframeList extends ArrayList { * @param realTime The value to use * @return The first Keyframe after the given value */ - public T getNextKeyframe(int realTime) { + public Keyframe getNextKeyframe(int realTime) { if(this.isEmpty()) return null; - T backup = null; + Keyframe backup = null; - for(T kf : this) { + for(Keyframe kf : this) { if(kf.getRealTimestamp() > realTime) { return kf; //first found element is next @@ -99,18 +104,18 @@ public class KeyframeList extends ArrayList { * @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 found = new ArrayList(); - for(T kf : this) { + public Keyframe getClosestKeyframeForTimestamp(int realTime, int tolerance) { + List> found = new ArrayList>(); + for(Keyframe kf : this) { if(!(kf.getValue() instanceof Position)) continue; if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { found.add(kf); } } - T closest = null; + Keyframe closest = null; - for(T kf : found) { + for(Keyframe kf : found) { if(closest == null || Math.abs(closest.getRealTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) { closest = kf; } @@ -118,23 +123,45 @@ public class KeyframeList extends ArrayList { return closest; } - public T first() { + public Keyframe first() { if(isEmpty()) return null; return get(0); } - public T last() { + public Keyframe 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() : new GenericSplineInterpolation(); + + for(Keyframe 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); diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java index e8328504..7b0f15bd 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java @@ -7,6 +7,8 @@ package eu.crushedpixel.replaymod.interpolation; *

* It is recommended for KeyframeValue subclasses to have a @NoArgsConstructor annotation. */ -public abstract class KeyframeValue { +public interface KeyframeValue { + + public KeyframeValue newInstance(); } diff --git a/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java b/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java index 91669b5f..cd2c32ef 100644 --- a/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java @@ -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 spline = new GenericSplineInterpolation(); - private DistanceComparator distanceComparator = new DistanceComparator(); - private List> keyframes = new ArrayList>(); + private KeyframeList 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 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> distanceSorted = new ArrayList>(keyframes); + Collections.sort(distanceSorted, distanceComparator); - for(Keyframe kf : keyframes) { + + for(Keyframe kf : distanceSorted) { drawPoint(doubleX, doubleY, doubleZ, kf); } @@ -102,18 +98,7 @@ public class PathPreviewRenderer { @SubscribeEvent public void recalcSpline(KeyframesModifyEvent event) { - keyframes = new ArrayList>(); - spline = new GenericSplineInterpolation(); - for(Keyframe kf : event.getPositionKeyframes()) { - Keyframe pkf = (Keyframe)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> { diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java index 88e25287..6e42a7ff 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java @@ -50,8 +50,8 @@ public class ReplayHandler { private static boolean inPath = false; private static CameraEntity cameraEntity; - private static KeyframeList> positionKeyframes = new KeyframeList>(); - private static KeyframeList> timeKeyframes = new KeyframeList>(); + private static KeyframeList positionKeyframes = new KeyframeList(); + private static KeyframeList timeKeyframes = new KeyframeList(); private static boolean inReplay = false; private static Entity currentEntity = null; @@ -337,16 +337,16 @@ public class ReplayHandler { return backup; } - public static KeyframeList> getPositionKeyframes() { + public static KeyframeList getPositionKeyframes() { return positionKeyframes; } - public static KeyframeList> getTimeKeyframes() { + public static KeyframeList getTimeKeyframes() { return timeKeyframes; } - public static KeyframeList getAllKeyframes() { - KeyframeList keyframeList = new KeyframeList(); + public static ArrayList getAllKeyframes() { + ArrayList keyframeList = new ArrayList(); keyframeList.addAll(positionKeyframes); keyframeList.addAll(timeKeyframes); diff --git a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java index 39b30345..b0ce9487 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java @@ -205,7 +205,7 @@ public class VideoRenderer { } private void updateCam() { - KeyframeList> positionKeyframes = ReplayHandler.getPositionKeyframes(); + KeyframeList 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> timeKeyframes = ReplayHandler.getTimeKeyframes(); + KeyframeList timeKeyframes = ReplayHandler.getTimeKeyframes(); int videoTime = framesDone * 1000 / fps; int timeCount = timeKeyframes.size();