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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,9 +7,14 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class TimestampValue extends KeyframeValue { public class TimestampValue implements KeyframeValue {
@Interpolate @Interpolate
public double value; 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.ArrayList;
import java.util.List; 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 static final KeyframeComparator KEYFRAME_COMPARATOR = new KeyframeComparator();
private Boolean previousCallLinear = null;
private Interpolation<K> interpolation;
@Override @Override
public boolean add(T t) { public boolean add(Keyframe<K> t) {
boolean success = super.add(t); boolean success = super.add(t);
sort(); sort();
return success; return success;
} }
@Override @Override
public void add(int index, T element) { public void add(int index, Keyframe<K> element) {
super.add(index, element); super.add(index, element);
sort(); sort();
} }
@Override @Override
public T remove(int index) { public Keyframe<K> remove(int index) {
T removed = super.remove(index); Keyframe<K> removed = super.remove(index);
sort(); sort();
return removed; return removed;
} }
@@ -39,6 +43,7 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
} }
public void sort() { public void sort() {
previousCallLinear = null;
sort(KEYFRAME_COMPARATOR); sort(KEYFRAME_COMPARATOR);
} }
@@ -47,14 +52,14 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
* @param realTime The value to use * @param realTime The value to use
* @return The first Keyframe prior to the given value * @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; 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) { if(kf.getRealTimestamp() < realTime) {
found.add(kf); found.add(kf);
@@ -75,12 +80,12 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
* @param realTime The value to use * @param realTime The value to use
* @return The first Keyframe after the given value * @return The first Keyframe after the given value
*/ */
public T getNextKeyframe(int realTime) { public Keyframe<K> getNextKeyframe(int realTime) {
if(this.isEmpty()) return null; 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) { if(kf.getRealTimestamp() > realTime) {
return kf; //first found element is next 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 * @param tolerance The threshold to allow for close Keyframes
* @return The closest Keyframe, or null if no Keyframe within treshold * @return The closest Keyframe, or null if no Keyframe within treshold
*/ */
public T getClosestKeyframeForTimestamp(int realTime, int tolerance) { public Keyframe<K> getClosestKeyframeForTimestamp(int realTime, int tolerance) {
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.getValue() instanceof Position)) continue; if(!(kf.getValue() instanceof Position)) continue;
if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) {
found.add(kf); 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)) { if(closest == null || Math.abs(closest.getRealTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) {
closest = kf; closest = kf;
} }
@@ -118,23 +123,45 @@ public class KeyframeList<T extends Keyframe> extends ArrayList<T> {
return closest; return closest;
} }
public T first() { public Keyframe<K> first() {
if(isEmpty()) return null; if(isEmpty()) return null;
return get(0); return get(0);
} }
public T last() { public Keyframe<K> last() {
if(isEmpty()) return null; if(isEmpty()) return null;
return get(size()-1); 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 * Returns a value between 0 and 1, representing the number that should be passed
* to Interpolation#getValue() calls on this list of Keyframes. * to Interpolation#getValue() calls on this list of Keyframes.
* @param timestamp The value to use * @param timestamp The value to use
* @return A value between 0 and 1 * @return A value between 0 and 1
*/ */
public float getPositionOnSpline(int timestamp) { private float getPositionOnPath(int timestamp) {
Keyframe previousKeyframe = getPreviousKeyframe(timestamp); Keyframe previousKeyframe = getPreviousKeyframe(timestamp);
Keyframe nextKeyframe = getNextKeyframe(timestamp); Keyframe nextKeyframe = getNextKeyframe(timestamp);

View File

@@ -7,6 +7,8 @@ package eu.crushedpixel.replaymod.interpolation;
* <br><br> * <br><br>
* It is recommended for KeyframeValue subclasses to have a @NoArgsConstructor annotation. * 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.gui.overlay.GuiReplayOverlay;
import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.Position; 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 eu.crushedpixel.replaymod.replay.ReplayHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
@@ -26,11 +26,9 @@ public class PathPreviewRenderer {
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = Minecraft.getMinecraft();
private GenericSplineInterpolation<Position> spline = new GenericSplineInterpolation<Position>();
private DistanceComparator distanceComparator = new DistanceComparator(); private DistanceComparator distanceComparator = new DistanceComparator();
private List<Keyframe<Position>> keyframes = new ArrayList<Keyframe<Position>>(); private KeyframeList<Position> keyframes;
@SubscribeEvent @SubscribeEvent
public void renderCameraPath(RenderWorldLastEvent event) { public void renderCameraPath(RenderWorldLastEvent event) {
@@ -51,28 +49,25 @@ public class PathPreviewRenderer {
GlStateManager.disableTexture2D(); GlStateManager.disableTexture2D();
if(spline.getPoints().size() > 1) { if(keyframes.size() > 1) {
Position prev = null; Position prev = null;
if(ReplayMod.replaySettings.isLinearMovement()) { if(ReplayMod.replaySettings.isLinearMovement()) {
for(int i = 0; i < spline.getPoints().size(); i++) { for(Keyframe<Position> point : keyframes) {
Position point = spline.getPoints().get(i);
if(prev != null) { 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 { } else {
float max = spline.getPoints().size() * 50; float max = keyframes.size() * 50;
for(int i = 0; i < max; i++) { for(int i = 0; i < max; i++) {
Position point = new Position(); Position point = keyframes.getInterpolatedValueForPathPosition(i/max, false);
spline.applyPoint(i / max, point);
if(prev != null) { if(prev != null) {
drawConnection(doubleX, doubleY, doubleZ, prev, point, Color.RED.getRGB()); drawConnection(doubleX, doubleY, doubleZ, prev, point, Color.RED.getRGB());
@@ -83,12 +78,13 @@ public class PathPreviewRenderer {
} }
} }
distanceComparator.setPlayerPos(doubleX, doubleY + 1.4, doubleZ); 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); drawPoint(doubleX, doubleY, doubleZ, kf);
} }
@@ -102,18 +98,7 @@ public class PathPreviewRenderer {
@SubscribeEvent @SubscribeEvent
public void recalcSpline(KeyframesModifyEvent event) { public void recalcSpline(KeyframesModifyEvent event) {
keyframes = new ArrayList<Keyframe<Position>>(); keyframes = event.getPositionKeyframes();
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();
}
} }
private class DistanceComparator implements Comparator<Keyframe<Position>> { private class DistanceComparator implements Comparator<Keyframe<Position>> {

View File

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

View File

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