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
This commit is contained in:
@@ -7,51 +7,26 @@ import eu.crushedpixel.replaymod.holders.TimestampValue;
|
|||||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ListIterator;
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition> {
|
public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition> {
|
||||||
|
|
||||||
private Map<List<Integer>, SpectatorDataInterpolation> spectatorDataInterpolators;
|
private KeyframeList<AdvancedPosition> completedKeyframes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AdvancedPosition getInterpolatedValueForPathPosition(float pathPosition, boolean linear) {
|
public AdvancedPosition getInterpolatedValueForPathPosition(float pathPosition, boolean linear) {
|
||||||
if(first() == null) return null;
|
if(first() == null) return null;
|
||||||
if(size() == 1) return first().getValue();
|
if(size() == 1) return first().getValue();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
AdvancedPosition toApply = first().getValue().newInstance();
|
|
||||||
|
|
||||||
if(previousCallLinear != (Boolean)linear) {
|
if(previousCallLinear != (Boolean)linear) {
|
||||||
recalculate(linear);
|
recalculate(linear);
|
||||||
}
|
}
|
||||||
|
|
||||||
int keyframeIndex = (int)Math.min(size()-1, pathPosition*(size()-1));
|
//convert the path position to match the completedKeyframes list
|
||||||
float remainder = (pathPosition - ((float)keyframeIndex/(size()-1)));
|
int timestamp = getTimestampFromPathPosition(pathPosition);
|
||||||
float partial = remainder / (1f/(size()-1));
|
float newPathPosition = completedKeyframes.getPositionOnPath(timestamp);
|
||||||
|
return completedKeyframes.getInterpolatedValueForPathPosition(newPathPosition, linear);
|
||||||
AdvancedPosition current = get(keyframeIndex).getValue();
|
|
||||||
|
|
||||||
SpectatorDataInterpolation dataInterpolation = null;
|
|
||||||
if(current instanceof SpectatorData) {
|
|
||||||
for(Entry<List<Integer>, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,15 +36,7 @@ public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition>
|
|||||||
|
|
||||||
if(size() < 2) return;
|
if(size() < 2) return;
|
||||||
|
|
||||||
interpolation = linear ? first().getValue().getLinearInterpolator() : first().getValue().getCubicInterpolator();
|
completedKeyframes = new KeyframeList<AdvancedPosition>();
|
||||||
|
|
||||||
for(Keyframe<AdvancedPosition> keyframe : this) {
|
|
||||||
interpolation.addPoint(keyframe.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
interpolation.prepare();
|
|
||||||
|
|
||||||
spectatorDataInterpolators = new HashMap<List<Integer>, SpectatorDataInterpolation>();
|
|
||||||
|
|
||||||
//find subsequent Spectator Keyframes with the same Entity ID
|
//find subsequent Spectator Keyframes with the same Entity ID
|
||||||
ListIterator<Keyframe<AdvancedPosition>> iterator = this.listIterator();
|
ListIterator<Keyframe<AdvancedPosition>> iterator = this.listIterator();
|
||||||
@@ -78,9 +45,12 @@ public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition>
|
|||||||
SpectatorDataInterpolation spectatorInterpolation = null;
|
SpectatorDataInterpolation spectatorInterpolation = null;
|
||||||
|
|
||||||
boolean found = keyframe.getValue() instanceof SpectatorData;
|
boolean found = keyframe.getValue() instanceof SpectatorData;
|
||||||
int first = iterator.nextIndex()-1;
|
|
||||||
int firstTimestamp = -1;
|
int firstTimestamp = -1;
|
||||||
|
|
||||||
|
if(!found) {
|
||||||
|
completedKeyframes.add(keyframe);
|
||||||
|
}
|
||||||
|
|
||||||
while(found) {
|
while(found) {
|
||||||
if(spectatorInterpolation == null) {
|
if(spectatorInterpolation == null) {
|
||||||
spectatorInterpolation = new SpectatorDataInterpolation(linear);
|
spectatorInterpolation = new SpectatorDataInterpolation(linear);
|
||||||
@@ -89,10 +59,11 @@ public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition>
|
|||||||
TimestampValue timestampValue = ReplayHandler.getTimeKeyframes().getInterpolatedValueForTimestamp(keyframeTimestamp, true);
|
TimestampValue timestampValue = ReplayHandler.getTimeKeyframes().getInterpolatedValueForTimestamp(keyframeTimestamp, true);
|
||||||
int replayTimestamp = timestampValue == null ? 0 : timestampValue.asInt();
|
int replayTimestamp = timestampValue == null ? 0 : timestampValue.asInt();
|
||||||
if(firstTimestamp == -1) firstTimestamp = replayTimestamp;
|
if(firstTimestamp == -1) firstTimestamp = replayTimestamp;
|
||||||
spectatorInterpolation.addPoint((SpectatorData)keyframe.getValue(), replayTimestamp);
|
spectatorInterpolation.addPoint(keyframe, replayTimestamp);
|
||||||
if(iterator.hasNext()) {
|
if(iterator.hasNext()) {
|
||||||
keyframe = iterator.next();
|
keyframe = iterator.next();
|
||||||
found = keyframe.getValue() instanceof SpectatorData;
|
found = keyframe.getValue() instanceof SpectatorData;
|
||||||
|
if(!found) completedKeyframes.add(keyframe);
|
||||||
} else {
|
} else {
|
||||||
found = false;
|
found = false;
|
||||||
}
|
}
|
||||||
@@ -100,11 +71,7 @@ public class AdvancedPositionKeyframeList extends KeyframeList<AdvancedPosition>
|
|||||||
|
|
||||||
if(spectatorInterpolation != null && spectatorInterpolation.size() > 1) {
|
if(spectatorInterpolation != null && spectatorInterpolation.size() > 1) {
|
||||||
spectatorInterpolation.prepare();
|
spectatorInterpolation.prepare();
|
||||||
List<Integer> keys = new ArrayList<Integer>();
|
completedKeyframes.addAll(spectatorInterpolation.elements());
|
||||||
for(int i=first; i<first+spectatorInterpolation.size()-1; i++) {
|
|
||||||
keys.add(i);
|
|
||||||
}
|
|
||||||
spectatorDataInterpolators.put(keys, spectatorInterpolation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import eu.crushedpixel.replaymod.holders.KeyframeComparator;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -43,6 +44,20 @@ public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>
|
|||||||
sort();
|
sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends Keyframe<K>> c) {
|
||||||
|
boolean result = super.addAll(c);
|
||||||
|
sort();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, Collection<? extends Keyframe<K>> c) {
|
||||||
|
boolean result = super.addAll(index, c);
|
||||||
|
sort();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Keyframe<K> remove(int index) {
|
public Keyframe<K> remove(int index) {
|
||||||
Keyframe<K> removed = super.remove(index);
|
Keyframe<K> removed = super.remove(index);
|
||||||
@@ -219,4 +234,16 @@ public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>
|
|||||||
return Math.max(0, Math.min(1, value));
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class SpectatorDataInterpolation {
|
|||||||
|
|
||||||
private Integer entityID = null;
|
private Integer entityID = null;
|
||||||
|
|
||||||
private List<Pair<Integer, SpectatorData>> points = new ArrayList<Pair<Integer, SpectatorData>>();
|
private List<Pair<Integer, Keyframe<AdvancedPosition>>> points = new ArrayList<Pair<Integer, Keyframe<AdvancedPosition>>>();
|
||||||
private KeyframeList<AdvancedPosition> underlyingKeyframes;
|
private KeyframeList<AdvancedPosition> underlyingKeyframes;
|
||||||
|
|
||||||
private final boolean linear;
|
private final boolean linear;
|
||||||
@@ -32,18 +32,8 @@ public class SpectatorDataInterpolation {
|
|||||||
return points.size();
|
return points.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(SpectatorData spectatorData) {
|
public List<Keyframe<AdvancedPosition>> elements() {
|
||||||
return indexOf(spectatorData) != -1;
|
return new ArrayList<Keyframe<AdvancedPosition>>(underlyingKeyframes);
|
||||||
}
|
|
||||||
|
|
||||||
public int indexOf(SpectatorData spectatorData) {
|
|
||||||
int i = 0;
|
|
||||||
for(Pair<Integer, SpectatorData> pair : points) {
|
|
||||||
if(pair.getValue().equals(spectatorData)) return i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepare() {
|
public void prepare() {
|
||||||
@@ -54,43 +44,72 @@ public class SpectatorDataInterpolation {
|
|||||||
new GenericSplineInterpolation<SpectatorDataThirdPersonInfo>();
|
new GenericSplineInterpolation<SpectatorDataThirdPersonInfo>();
|
||||||
|
|
||||||
double previousSmoothness = 0.5;
|
double previousSmoothness = 0.5;
|
||||||
for(Pair<Integer, SpectatorData> pair : points) {
|
for(Pair<Integer, Keyframe<AdvancedPosition>> pair : points) {
|
||||||
if(pair.getValue().getSpectatingMethod() == SpectatorData.SpectatingMethod.FIRST_PERSON) {
|
if(((SpectatorData)pair.getValue().getValue()).getSpectatingMethod() == SpectatorData.SpectatingMethod.FIRST_PERSON) {
|
||||||
thirdPersonInfoInterpolation.addPoint(new SpectatorDataThirdPersonInfo(0, 0, 0, previousSmoothness));
|
thirdPersonInfoInterpolation.addPoint(new SpectatorDataThirdPersonInfo(0, 0, 0, previousSmoothness));
|
||||||
} else {
|
} else {
|
||||||
SpectatorDataThirdPersonInfo thirdPersonInfo = pair.getValue().getThirdPersonInfo();
|
SpectatorDataThirdPersonInfo thirdPersonInfo = ((SpectatorData)pair.getValue().getValue()).getThirdPersonInfo();
|
||||||
thirdPersonInfoInterpolation.addPoint(thirdPersonInfo);
|
thirdPersonInfoInterpolation.addPoint(thirdPersonInfo);
|
||||||
previousSmoothness = thirdPersonInfo.shoulderCamSmoothness;
|
previousSmoothness = thirdPersonInfo.shoulderCamSmoothness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thirdPersonInfoInterpolation.prepare();
|
thirdPersonInfoInterpolation.prepare();
|
||||||
|
|
||||||
|
//updating the spectator keyframe's position in the world to smoothly continue the path
|
||||||
|
//with non-spectator position keyframes
|
||||||
|
for(Pair<Integer, Keyframe<AdvancedPosition>> 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
|
//feed the underlying keyframe list with AdvancedPosition Keyframes that are derived from the Spectator Keyframes
|
||||||
underlyingKeyframes = new KeyframeList<AdvancedPosition>();
|
underlyingKeyframes = new KeyframeList<AdvancedPosition>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int firstTimestamp = -1;
|
int firstTimestamp = -1;
|
||||||
int size = points.size()-1;
|
int size = points.size()-1;
|
||||||
for(Pair<Integer, SpectatorData> pair : points) {
|
for(Pair<Integer, Keyframe<AdvancedPosition>> pair : points) {
|
||||||
|
underlyingKeyframes.add(new Keyframe<AdvancedPosition>(pair.getValue().getRealTimestamp(), pair.getValue().getValue()));
|
||||||
|
|
||||||
int timestamp = pair.getKey();
|
int timestamp = pair.getKey();
|
||||||
if(firstTimestamp == -1) firstTimestamp = timestamp;
|
if(firstTimestamp == -1) firstTimestamp = timestamp;
|
||||||
|
|
||||||
|
int realTimestamp = pair.getValue().getRealTimestamp();
|
||||||
|
int nextRealTimestamp = realTimestamp;
|
||||||
|
|
||||||
int currentTimestamp = timestamp;
|
int currentTimestamp = timestamp;
|
||||||
int nextTimestamp = timestamp;
|
int nextTimestamp = timestamp;
|
||||||
|
|
||||||
if(i+1 < points.size()) {
|
if(i+1 < points.size()) {
|
||||||
nextTimestamp = points.get(i+1).getKey();
|
Pair<Integer, Keyframe<AdvancedPosition>> nextPair = points.get(i+1);
|
||||||
|
nextTimestamp = nextPair.getKey();
|
||||||
|
nextRealTimestamp = nextPair.getValue().getRealTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
int difference = nextTimestamp - timestamp;
|
int difference = nextTimestamp - timestamp;
|
||||||
|
int realTimestampDifference = nextRealTimestamp - realTimestamp;
|
||||||
|
|
||||||
int smoothness = 0;
|
int smoothness = 0;
|
||||||
while(currentTimestamp + smoothness <= nextTimestamp) {
|
while(currentTimestamp + smoothness < nextTimestamp) {
|
||||||
currentTimestamp += smoothness;
|
currentTimestamp += smoothness;
|
||||||
|
|
||||||
SpectatorDataThirdPersonInfo thirdPersonInfo = new SpectatorDataThirdPersonInfo();
|
SpectatorDataThirdPersonInfo thirdPersonInfo = new SpectatorDataThirdPersonInfo();
|
||||||
float percentage = (float)i/size;
|
float percentage = (float)i/size;
|
||||||
percentage += ((currentTimestamp-timestamp)/(float)difference) * 1f/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);
|
thirdPersonInfoInterpolation.applyPoint(percentage, thirdPersonInfo);
|
||||||
|
|
||||||
@@ -106,52 +125,19 @@ public class SpectatorDataInterpolation {
|
|||||||
|
|
||||||
//next, move the camera point to fulfill the specified distance to the entity
|
//next, move the camera point to fulfill the specified distance to the entity
|
||||||
entityPosition = entityPosition.getDestination(-1 * thirdPersonInfo.shoulderCamDistance);
|
entityPosition = entityPosition.getDestination(-1 * thirdPersonInfo.shoulderCamDistance);
|
||||||
underlyingKeyframes.add(new Keyframe<AdvancedPosition>(currentTimestamp-firstTimestamp, entityPosition));
|
|
||||||
|
underlyingKeyframes.add(new Keyframe<AdvancedPosition>(interpolatedRealTimestamp, entityPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyPoint(float position, AdvancedPosition toEdit) {
|
public void addPoint(Keyframe<AdvancedPosition> keyframe, int realTimestamp) {
|
||||||
int keyframeIndex = (int)Math.min(size()-1, position*(size()-1));
|
if(!(keyframe.getValue() instanceof SpectatorData)) throw new IllegalArgumentException();
|
||||||
float remainder = (position - ((float)keyframeIndex/(size()-1)));
|
SpectatorData spectatorData = (SpectatorData)keyframe.getValue();
|
||||||
float partial = remainder / (1f/(size()-1));
|
|
||||||
|
|
||||||
Pair<Integer, SpectatorData> pair = points.get(keyframeIndex);
|
|
||||||
Pair<Integer, SpectatorData> 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) {
|
|
||||||
if(entityID == null) entityID = spectatorData.getSpectatedEntityID();
|
if(entityID == null) entityID = spectatorData.getSpectatedEntityID();
|
||||||
else if(entityID != spectatorData.getSpectatedEntityID()) throw new IllegalArgumentException();
|
else if(entityID != spectatorData.getSpectatedEntityID()) throw new IllegalArgumentException();
|
||||||
points.add(Pair.of(realTimestamp, spectatorData));
|
points.add(Pair.of(realTimestamp, keyframe));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user