From 0ed3f95f6ca8b19540bbfd744cad788ddcfee3bf Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Thu, 10 Sep 2015 17:49:18 +0200 Subject: [PATCH] SpectatorDataInterpolation code cleanup (+1 squashed commit) Squashed commits: [6aa2eef] To ensure a continuous camera path, the SpectatorDataInterpolation now directly influences the AdvancedPositionKeyframeList's interpolation's Position Keyframes --- .../AdvancedPositionKeyframeList.java | 61 +++------- .../replaymod/interpolation/KeyframeList.java | 27 +++++ .../SpectatorDataInterpolation.java | 104 ++++++++---------- 3 files changed, 86 insertions(+), 106 deletions(-) diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/AdvancedPositionKeyframeList.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/AdvancedPositionKeyframeList.java index 79251311..c27bdfa6 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/AdvancedPositionKeyframeList.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/AdvancedPositionKeyframeList.java @@ -7,51 +7,26 @@ import eu.crushedpixel.replaymod.holders.TimestampValue; import eu.crushedpixel.replaymod.replay.ReplayHandler; import lombok.NoArgsConstructor; -import java.util.*; -import java.util.Map.Entry; +import java.util.ListIterator; @NoArgsConstructor public class AdvancedPositionKeyframeList extends KeyframeList { - private Map, SpectatorDataInterpolation> spectatorDataInterpolators; + private KeyframeList completedKeyframes; @Override public AdvancedPosition getInterpolatedValueForPathPosition(float pathPosition, boolean linear) { if(first() == null) return null; if(size() == 1) return first().getValue(); - @SuppressWarnings("unchecked") - AdvancedPosition toApply = first().getValue().newInstance(); - if(previousCallLinear != (Boolean)linear) { recalculate(linear); } - int keyframeIndex = (int)Math.min(size()-1, pathPosition*(size()-1)); - float remainder = (pathPosition - ((float)keyframeIndex/(size()-1))); - float partial = remainder / (1f/(size()-1)); - - AdvancedPosition current = get(keyframeIndex).getValue(); - - SpectatorDataInterpolation dataInterpolation = null; - if(current instanceof SpectatorData) { - for(Entry, SpectatorDataInterpolation> entry : spectatorDataInterpolators.entrySet()) { - if(entry.getKey().contains(keyframeIndex)) dataInterpolation = entry.getValue(); - } - } - - if(dataInterpolation == null) { - interpolation.applyPoint(pathPosition, toApply); - } else { - SpectatorData spectatorData = (SpectatorData)current; - int index = dataInterpolation.indexOf(spectatorData); - int size = dataInterpolation.size()-1; - float one = 1f/size; - float pathPos = (index/(float)size)+(partial*one); - dataInterpolation.applyPoint(pathPos, toApply); - } - - return toApply; + //convert the path position to match the completedKeyframes list + int timestamp = getTimestampFromPathPosition(pathPosition); + float newPathPosition = completedKeyframes.getPositionOnPath(timestamp); + return completedKeyframes.getInterpolatedValueForPathPosition(newPathPosition, linear); } @Override @@ -61,15 +36,7 @@ public class AdvancedPositionKeyframeList extends KeyframeList if(size() < 2) return; - interpolation = linear ? first().getValue().getLinearInterpolator() : first().getValue().getCubicInterpolator(); - - for(Keyframe keyframe : this) { - interpolation.addPoint(keyframe.getValue()); - } - - interpolation.prepare(); - - spectatorDataInterpolators = new HashMap, SpectatorDataInterpolation>(); + completedKeyframes = new KeyframeList(); //find subsequent Spectator Keyframes with the same Entity ID ListIterator> iterator = this.listIterator(); @@ -78,9 +45,12 @@ public class AdvancedPositionKeyframeList extends KeyframeList SpectatorDataInterpolation spectatorInterpolation = null; boolean found = keyframe.getValue() instanceof SpectatorData; - int first = iterator.nextIndex()-1; int firstTimestamp = -1; + if(!found) { + completedKeyframes.add(keyframe); + } + while(found) { if(spectatorInterpolation == null) { spectatorInterpolation = new SpectatorDataInterpolation(linear); @@ -89,10 +59,11 @@ public class AdvancedPositionKeyframeList extends KeyframeList TimestampValue timestampValue = ReplayHandler.getTimeKeyframes().getInterpolatedValueForTimestamp(keyframeTimestamp, true); int replayTimestamp = timestampValue == null ? 0 : timestampValue.asInt(); if(firstTimestamp == -1) firstTimestamp = replayTimestamp; - spectatorInterpolation.addPoint((SpectatorData)keyframe.getValue(), replayTimestamp); + spectatorInterpolation.addPoint(keyframe, replayTimestamp); if(iterator.hasNext()) { keyframe = iterator.next(); found = keyframe.getValue() instanceof SpectatorData; + if(!found) completedKeyframes.add(keyframe); } else { found = false; } @@ -100,11 +71,7 @@ public class AdvancedPositionKeyframeList extends KeyframeList if(spectatorInterpolation != null && spectatorInterpolation.size() > 1) { spectatorInterpolation.prepare(); - List keys = new ArrayList(); - for(int i=first; i extends ArrayList sort(); } + @Override + public boolean addAll(Collection> c) { + boolean result = super.addAll(c); + sort(); + return result; + } + + @Override + public boolean addAll(int index, Collection> c) { + boolean result = super.addAll(index, c); + sort(); + return result; + } + @Override public Keyframe remove(int index) { Keyframe removed = super.remove(index); @@ -219,4 +234,16 @@ public class KeyframeList extends ArrayList return Math.max(0, Math.min(1, value)); } + protected int getTimestampFromPathPosition(float pathPosition) { + int keyframeIndex = (int)Math.min(size()-1, pathPosition*(size()-1)); + float remainder = (pathPosition - ((float)keyframeIndex/(size()-1))); + float partial = remainder / (1f/(size()-1)); + + int previousTimestamp = get(keyframeIndex).getRealTimestamp(); + int nextTimestamp = size()-1 > keyframeIndex ? get(keyframeIndex+1).getRealTimestamp() : previousTimestamp; + + int diff = nextTimestamp - previousTimestamp; + return (int)(previousTimestamp + (partial*diff)); + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/SpectatorDataInterpolation.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/SpectatorDataInterpolation.java index a682260f..86f61be7 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/SpectatorDataInterpolation.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/SpectatorDataInterpolation.java @@ -18,7 +18,7 @@ public class SpectatorDataInterpolation { private Integer entityID = null; - private List> points = new ArrayList>(); + private List>> points = new ArrayList>>(); private KeyframeList underlyingKeyframes; private final boolean linear; @@ -32,18 +32,8 @@ public class SpectatorDataInterpolation { return points.size(); } - public boolean contains(SpectatorData spectatorData) { - return indexOf(spectatorData) != -1; - } - - public int indexOf(SpectatorData spectatorData) { - int i = 0; - for(Pair pair : points) { - if(pair.getValue().equals(spectatorData)) return i; - i++; - } - - return -1; + public List> elements() { + return new ArrayList>(underlyingKeyframes); } public void prepare() { @@ -54,43 +44,72 @@ public class SpectatorDataInterpolation { new GenericSplineInterpolation(); double previousSmoothness = 0.5; - for(Pair pair : points) { - if(pair.getValue().getSpectatingMethod() == SpectatorData.SpectatingMethod.FIRST_PERSON) { + for(Pair> pair : points) { + if(((SpectatorData)pair.getValue().getValue()).getSpectatingMethod() == SpectatorData.SpectatingMethod.FIRST_PERSON) { thirdPersonInfoInterpolation.addPoint(new SpectatorDataThirdPersonInfo(0, 0, 0, previousSmoothness)); } else { - SpectatorDataThirdPersonInfo thirdPersonInfo = pair.getValue().getThirdPersonInfo(); + SpectatorDataThirdPersonInfo thirdPersonInfo = ((SpectatorData)pair.getValue().getValue()).getThirdPersonInfo(); thirdPersonInfoInterpolation.addPoint(thirdPersonInfo); previousSmoothness = thirdPersonInfo.shoulderCamSmoothness; } } thirdPersonInfoInterpolation.prepare(); + //updating the spectator keyframe's position in the world to smoothly continue the path + //with non-spectator position keyframes + for(Pair> pair : points) { + AdvancedPosition entityPosition = ReplayHandler.getEntityPositionTracker().getEntityPositionAtTimestamp(entityID, pair.getKey()); + if(entityPosition == null) continue; + + //transform the entity position (sry for no dry code :x ) + SpectatorDataThirdPersonInfo thirdPersonInfo = ((SpectatorData)pair.getValue().getValue()).getThirdPersonInfo(); + + //first, rotate the camera pitch and yaw according to the settings + entityPosition.setYaw(entityPosition.getYaw() + thirdPersonInfo.shoulderCamYawOffset); + entityPosition.setPitch(entityPosition.getPitch() + thirdPersonInfo.shoulderCamPitchOffset); + + //next, move the camera point to fulfill the specified distance to the entity + entityPosition = entityPosition.getDestination(-1 * thirdPersonInfo.shoulderCamDistance); + + pair.getValue().getValue().apply(entityPosition); + } + //feed the underlying keyframe list with AdvancedPosition Keyframes that are derived from the Spectator Keyframes underlyingKeyframes = new KeyframeList(); int i = 0; int firstTimestamp = -1; int size = points.size()-1; - for(Pair pair : points) { + for(Pair> pair : points) { + underlyingKeyframes.add(new Keyframe(pair.getValue().getRealTimestamp(), pair.getValue().getValue())); + int timestamp = pair.getKey(); if(firstTimestamp == -1) firstTimestamp = timestamp; + int realTimestamp = pair.getValue().getRealTimestamp(); + int nextRealTimestamp = realTimestamp; + int currentTimestamp = timestamp; int nextTimestamp = timestamp; if(i+1 < points.size()) { - nextTimestamp = points.get(i+1).getKey(); + Pair> nextPair = points.get(i+1); + nextTimestamp = nextPair.getKey(); + nextRealTimestamp = nextPair.getValue().getRealTimestamp(); } int difference = nextTimestamp - timestamp; + int realTimestampDifference = nextRealTimestamp - realTimestamp; int smoothness = 0; - while(currentTimestamp + smoothness <= nextTimestamp) { + while(currentTimestamp + smoothness < nextTimestamp) { currentTimestamp += smoothness; SpectatorDataThirdPersonInfo thirdPersonInfo = new SpectatorDataThirdPersonInfo(); float percentage = (float)i/size; percentage += ((currentTimestamp-timestamp)/(float)difference) * 1f/size; - if(Float.isNaN(percentage)) percentage = 1; + + float progress = ((currentTimestamp-timestamp)/(float)difference); + int interpolatedRealTimestamp = (int)(realTimestamp + (progress*realTimestampDifference)); thirdPersonInfoInterpolation.applyPoint(percentage, thirdPersonInfo); @@ -106,52 +125,19 @@ public class SpectatorDataInterpolation { //next, move the camera point to fulfill the specified distance to the entity entityPosition = entityPosition.getDestination(-1 * thirdPersonInfo.shoulderCamDistance); - underlyingKeyframes.add(new Keyframe(currentTimestamp-firstTimestamp, entityPosition)); + + underlyingKeyframes.add(new Keyframe(interpolatedRealTimestamp, entityPosition)); } i++; } } - public void applyPoint(float position, AdvancedPosition toEdit) { - int keyframeIndex = (int)Math.min(size()-1, position*(size()-1)); - float remainder = (position - ((float)keyframeIndex/(size()-1))); - float partial = remainder / (1f/(size()-1)); - - Pair pair = points.get(keyframeIndex); - Pair next = keyframeIndex < points.size()-1 ? points.get(keyframeIndex+1) : null; - - SpectatorData spectatorData = pair.getValue(); - - //decide whether the Entity is spectated in First Person Mode or not - boolean firstPerson = true; - - //firstPerson is false if either the SpectatorKeyframe - //or the following SpectatorKeyframe are no FIRST_PERSON Keyframes - //it's only true if the position value is between two FIRST_PERSON Keyframes - if(spectatorData.getSpectatingMethod() != SpectatorData.SpectatingMethod.FIRST_PERSON) { - firstPerson = false; - } else if(next != null) { - if(next.getValue().getSpectatingMethod() != SpectatorData.SpectatingMethod.FIRST_PERSON) firstPerson = false; - } - - if(firstPerson) { - int firstTimestamp = pair.getKey(); - int nextTimestamp = next == null ? pair.getKey() : next.getKey(); - - int diff = nextTimestamp - firstTimestamp; - int interpolatedTimestamp = firstTimestamp+(int)(partial*diff); - - AdvancedPosition pos = ReplayHandler.getEntityPositionTracker().getEntityPositionAtTimestamp(entityID, interpolatedTimestamp); - if(pos != null) toEdit.apply(pos); - } else { - toEdit.apply(underlyingKeyframes.getInterpolatedValueForPathPosition(position, linear)); - } - } - - public void addPoint(SpectatorData spectatorData, int realTimestamp) { + public void addPoint(Keyframe keyframe, int realTimestamp) { + if(!(keyframe.getValue() instanceof SpectatorData)) throw new IllegalArgumentException(); + SpectatorData spectatorData = (SpectatorData)keyframe.getValue(); if(entityID == null) entityID = spectatorData.getSpectatedEntityID(); else if(entityID != spectatorData.getSpectatedEntityID()) throw new IllegalArgumentException(); - points.add(Pair.of(realTimestamp, spectatorData)); + points.add(Pair.of(realTimestamp, keyframe)); } } \ No newline at end of file