Included Path Interpolation methods in KeyframeList and uses it in PathPreviewRenderer
Created newInstance() method in KeyframeValue interface
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user