From 5100b63964e8b561570d1f1c6405f6812dbe65d5 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Wed, 8 Jul 2015 04:13:24 +0200 Subject: [PATCH] Warning, GIGANTIC COMMIT! Replaced Keyframe subclasses with Generic Types of Keyframe and replaced all instanceof calls Replaced implementations of Linear and Spline interpolation to interpolate any Object that extends KeyframeValue and contains at least one public double Field. I'll need this for Keyframe interpolation in CustomImageObject Transformations Made MarkerKeyframe *no* subclass of Keyframe to avoid conflicts with PositionKeyframe in instanceof checks for Keyframe#getValue Created KeyframeList which extends ArrayList to provide some helping functions which DRY up the ReplayHandler Split up ReplayHandler's keyframeList into timeKeyframeList and positionKeyframeList --- .../replaymod/assets/AssetRepository.java | 4 + .../CustomImageObject.java | 58 ++- .../replaymod/entities/CameraEntity.java | 8 +- .../events/KeyframesModifyEvent.java | 15 +- .../replaymod/gui/GuiAssetAdder.java | 264 ------------- .../replaymod/gui/GuiEditKeyframe.java | 52 +-- .../replaymod/gui/GuiKeyframeRepository.java | 8 +- .../gui/elements/GuiKeyframeTimeline.java | 87 +---- .../gui/overlay/GuiReplayOverlay.java | 40 +- .../replaymod/holders/ExtendedPosition.java | 21 - .../replaymod/holders/Keyframe.java | 8 +- .../replaymod/holders/KeyframeSet.java | 20 +- .../replaymod/holders/MarkerKeyframe.java | 17 +- .../crushedpixel/replaymod/holders/Point.java | 13 + .../replaymod/holders/Position.java | 27 +- .../replaymod/holders/PositionKeyframe.java | 31 -- .../replaymod/holders/TimeKeyframe.java | 21 - .../replaymod/holders/TimestampValue.java | 13 + .../replaymod/holders/Transformations.java | 20 + .../GenericLinearInterpolation.java | 60 +++ .../GenericSplineInterpolation.java | 75 ++++ .../interpolation/Interpolation.java | 4 +- .../replaymod/interpolation/KeyframeList.java | 170 ++++++++ .../interpolation/KeyframeValue.java | 12 + .../interpolation/LinearInterpolation.java | 47 --- .../replaymod/interpolation/LinearPoint.java | 29 -- .../interpolation/LinearTimestamp.java | 19 - .../replaymod/interpolation/SplinePoint.java | 93 ----- .../replaymod/recording/PacketListener.java | 4 +- .../renderer/CustomObjectRenderer.java | 11 +- .../renderer/PathPreviewRenderer.java | 42 +- .../replaymod/replay/ReplayHandler.java | 368 ++++++------------ .../replaymod/replay/ReplayProcess.java | 95 ++--- .../replaymod/replay/ReplaySender.java | 34 +- .../replaymod/video/VideoRenderer.java | 92 +++-- 35 files changed, 765 insertions(+), 1117 deletions(-) rename src/main/java/eu/crushedpixel/replaymod/{holders => assets}/CustomImageObject.java (50%) delete mode 100644 src/main/java/eu/crushedpixel/replaymod/gui/GuiAssetAdder.java delete mode 100644 src/main/java/eu/crushedpixel/replaymod/holders/ExtendedPosition.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/holders/Point.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/holders/TimeKeyframe.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java create mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/GenericLinearInterpolation.java create mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/GenericSplineInterpolation.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/LinearInterpolation.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/LinearPoint.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/LinearTimestamp.java delete mode 100755 src/main/java/eu/crushedpixel/replaymod/interpolation/SplinePoint.java diff --git a/src/main/java/eu/crushedpixel/replaymod/assets/AssetRepository.java b/src/main/java/eu/crushedpixel/replaymod/assets/AssetRepository.java index 4fb42d61..b949f2f1 100644 --- a/src/main/java/eu/crushedpixel/replaymod/assets/AssetRepository.java +++ b/src/main/java/eu/crushedpixel/replaymod/assets/AssetRepository.java @@ -101,4 +101,8 @@ public class AssetRepository { return new ArrayList(replayAssets.values()); } + public ReplayAsset getAssetByUUID(UUID uuid) { + return replayAssets.get(uuid); + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/CustomImageObject.java b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java similarity index 50% rename from src/main/java/eu/crushedpixel/replaymod/holders/CustomImageObject.java rename to src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java index d5c8c269..62afce63 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/CustomImageObject.java +++ b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java @@ -1,37 +1,44 @@ -package eu.crushedpixel.replaymod.holders; +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.utils.RoundUtils; +import eu.crushedpixel.replaymod.replay.ReplayHandler; import lombok.Getter; import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.util.ResourceLocation; -import javax.imageio.ImageIO; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; +import java.util.UUID; public class CustomImageObject implements GuiEntryListEntry { - public CustomImageObject(Position position, String name, final File imageSource) throws IOException { - - this.position = new ExtendedPosition(RoundUtils.round(position.getX()), RoundUtils.round(position.getY()), RoundUtils.round(position.getZ()), 0, 0); - + public CustomImageObject(Transformations transformations, String name, UUID assetUUID) throws IOException { this.name = name; - setImageFile(imageSource); + setLinkedAsset(assetUUID); } - @Getter @Setter private ExtendedPosition position; @Getter @Setter private String name; - @Getter private File imageFile; + @Getter private UUID linkedAsset; - public void setImageFile(final File imageSource) throws IOException { - this.imageFile = imageSource; + @Getter @Setter private float width, height; - final BufferedImage bufferedImage = ImageIO.read(imageSource); + public void setLinkedAsset(UUID assetUUID) throws IOException { + ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(assetUUID); + + if(asset instanceof ReplayImageAsset) { + setImage(((ReplayImageAsset)asset).getObject()); + } else { + throw new UnsupportedOperationException("A CustomImageObject requires a ReplayImageAsset"); + } + } + + public void setImage(final BufferedImage bufferedImage) throws IOException { this.textureWidth = bufferedImage.getWidth(); this.textureHeight = bufferedImage.getHeight(); @@ -47,13 +54,13 @@ public class CustomImageObject implements GuiEntryListEntry { h = 1; } - this.position.setWidth(w); - this.position.setHeight(h); + this.setWidth(w); + this.setHeight(h); Minecraft.getMinecraft().addScheduledTask(new Runnable() { @Override public void run() { - resourceLocation = new ResourceLocation("customImages/"+imageSource.getAbsolutePath()); + resourceLocation = new ResourceLocation("customImages/"+linkedAsset.toString()); dynamicTexture = new DynamicTexture(bufferedImage); } }); @@ -78,4 +85,21 @@ public class CustomImageObject implements GuiEntryListEntry { public String getDisplayString() { return name; } + + /** + * Keyframing Code + */ + + private KeyframeList> anchorPointKeyframes, positionKeyframes, orientationKeyframes; + private KeyframeList> scaleKeyframes; + private KeyframeList> opacityKeyframes; + + private GenericSplineInterpolation anchorSpline, positionSpline, orientationSpline; + private GenericSplineInterpolation scaleSpline; + private GenericSplineInterpolation opacitySpline; + + public Transformations getTransformationsForTimestamp(int timestamp) { + return null; //TODO + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java b/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java index d4807e5c..2795da85 100755 --- a/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java +++ b/src/main/java/eu/crushedpixel/replaymod/entities/CameraEntity.java @@ -160,8 +160,8 @@ public class CameraEntity extends EntityPlayer { public void moveAbsolute(Position pos) { this.moveAbsolute(pos.getX(), pos.getY(), pos.getZ()); - rotationPitch = pos.getPitch(); - rotationYaw = pos.getYaw(); + rotationPitch = (float)pos.getPitch(); + rotationYaw = (float)pos.getYaw(); } public void moveAbsolute(double x, double y, double z) { @@ -181,8 +181,8 @@ public class CameraEntity extends EntityPlayer { } public void movePath(Position pos) { - this.prevRotationPitch = this.rotationPitch = pos.getPitch(); - this.prevRotationYaw = this.rotationYaw = pos.getYaw(); + this.prevRotationPitch = this.rotationPitch = (float)pos.getPitch(); + this.prevRotationYaw = this.rotationYaw = (float)pos.getYaw(); this.lastTickPosX = this.prevPosX = this.posX = pos.getX(); this.lastTickPosY = this.prevPosY = this.posY = pos.getY(); this.lastTickPosZ = this.prevPosZ = this.posZ = pos.getZ(); diff --git a/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java b/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java index 86dc5195..73bde9df 100644 --- a/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/KeyframesModifyEvent.java @@ -1,15 +1,18 @@ 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; +import lombok.AllArgsConstructor; +import lombok.Data; import net.minecraftforge.fml.common.eventhandler.Event; -import java.util.List; - +@Data +@AllArgsConstructor public class KeyframesModifyEvent extends Event { - public List keyframes; + private KeyframeList> positionKeyframes; + private KeyframeList> timeKeyframes; - public KeyframesModifyEvent(List keyframes) { - this.keyframes = keyframes; - } } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiAssetAdder.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiAssetAdder.java deleted file mode 100644 index 1143e8fc..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiAssetAdder.java +++ /dev/null @@ -1,264 +0,0 @@ -package eu.crushedpixel.replaymod.gui; - -import eu.crushedpixel.replaymod.ReplayMod; -import eu.crushedpixel.replaymod.gui.elements.*; -import eu.crushedpixel.replaymod.gui.elements.listeners.FileChooseListener; -import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener; -import eu.crushedpixel.replaymod.holders.CustomImageObject; -import eu.crushedpixel.replaymod.holders.Position; -import eu.crushedpixel.replaymod.replay.ReplayHandler; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.GuiTextField; -import net.minecraft.client.resources.I18n; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class GuiAssetAdder extends GuiScreen { - - private String screenTitle; - - private boolean initialized = false; - - private GuiEntryList objectList; - private GuiButton removeButton; - private GuiFileChooser addButton; - private GuiFileChooser guiFileChooser; - - private GuiAdvancedTextField nameInput; - - private GuiNumberInput xInput, yInput, zInput, yawInput, pitchInput, rollInput, scaleInput, opacityInput; - - private List textFields = new ArrayList(); - - public GuiAssetAdder() { - ReplayMod.replaySender.setReplaySpeed(0); - } - - @Override - public void initGui() { - if(!initialized) { - screenTitle = I18n.format("replaymod.gui.assets.title"); - - objectList = new GuiEntryList(fontRendererObj, 0, 0, 0, 0); - objectList.setEmptyMessage(I18n.format("replaymod.gui.assets.emptylist")); - - for(CustomImageObject object : ReplayHandler.getCustomImageObjects()) { - objectList.addElement(object); - } - - removeButton = new GuiButton(GuiConstants.ASSET_ADDER_REMOVE_BUTTON, 0, 0, I18n.format("replaymod.gui.remove")); - - addButton = new GuiFileChooser(1, 0, 0, I18n.format("replaymod.gui.add"), null, ImageIO.getReaderFileSuffixes()) { - @Override protected void updateDisplayString() {} - }; - - addButton.addFileChooseListener(new FileChooseListener() { - @Override - public void onFileChosen(File file) { - try { - objectList.addElement(new CustomImageObject(new Position(mc.getRenderViewEntity()), - I18n.format("replaymod.gui.assets.defaultname"), file)); - } catch(IOException e) { - e.printStackTrace(); - } - } - }); - - nameInput = new GuiAdvancedTextField(fontRendererObj, 2, 0, 0, 20); - nameInput.hint = I18n.format("replaymod.gui.assets.namehint"); - - xInput = new GuiNumberInput(3, fontRendererObj, 0, 0, 0, null, null, 0d, true); - yInput = new GuiNumberInput(4, fontRendererObj, 0, 0, 0, null, null, 0d, true); - zInput = new GuiNumberInput(5, fontRendererObj, 0, 0, 0, null, null, 0d, true); - - yawInput = new GuiNumberInput(6, fontRendererObj, 0, 0, 0, -360d, 360d, 0d, true); - pitchInput = new GuiNumberInput(7, fontRendererObj, 0, 0, 0, -360d, 360d, 0d, true); - rollInput = new GuiNumberInput(8, fontRendererObj, 0, 0, 0, -360d, 360d, 0d, true); - - scaleInput = new GuiNumberInputWithText(9, fontRendererObj, 0, 0, 0, 0d, 10000d, 100d, true, "%"); - opacityInput = new GuiNumberInputWithText(10, fontRendererObj, 0, 0, 0, 0d, 100d, 100d, true, "%"); - - guiFileChooser = new GuiFileChooser(3, 0, 0, I18n.format("replaymod.gui.assets.filechooser")+": ", null, ImageIO.getReaderFileSuffixes()); - guiFileChooser.addFileChooseListener(new FileChooseListener() { - @Override - public void onFileChosen(File file) { - try { - objectList.getElement(objectList.getSelectionIndex()).setImageFile(file); - } catch(Exception e) { - e.printStackTrace(); - } - } - }); - - guiFileChooser.enabled = false; - - textFields.add(objectList); - textFields.add(nameInput); - textFields.add(xInput); - textFields.add(yInput); - textFields.add(zInput); - textFields.add(pitchInput); - textFields.add(yawInput); - textFields.add(rollInput); - textFields.add(scaleInput); - textFields.add(opacityInput); - - objectList.addSelectionListener(new SelectionListener() { - @Override - public void onSelectionChanged(int selectionIndex) { - CustomImageObject object = objectList.getElement(selectionIndex); - if(object == null) { - guiFileChooser.enabled = false; - for(GuiTextField tf : textFields) { - tf.setEnabled(false); - } - return; - } - guiFileChooser.enabled = true; - for(GuiTextField tf : textFields) { - tf.setEnabled(true); - } - - nameInput.setText(object.getName()); - guiFileChooser.setSelectedFile(object.getImageFile()); - xInput.setValue(object.getPosition().getX()); - yInput.setValue(object.getPosition().getY()); - zInput.setValue(object.getPosition().getZ()); - pitchInput.setValue(object.getPosition().getPitch()); - yawInput.setValue(object.getPosition().getYaw()); - rollInput.setValue(object.getPosition().getRoll()); - scaleInput.setValue(object.getPosition().getScale()*100); - opacityInput.setValue(object.getPosition().getOpacity()*100); - } - }); - - if(objectList.getEntryCount() > 0) { - objectList.setSelectionIndex(0); - } - } - - int h = (int)Math.floor(((double)this.height-(45+20+15+20))/14); - - objectList.width = guiFileChooser.width = nameInput.width = 150; - objectList.xPosition = width/2 - objectList.width - 5; - objectList.setVisibleElements(h); - objectList.yPosition = 45; - - addButton.width = removeButton.width = 75; - addButton.xPosition = objectList.xPosition; - removeButton.xPosition = addButton.xPosition+addButton.width; - addButton.yPosition = removeButton.yPosition = objectList.yPosition + objectList.height + 2; - - nameInput.xPosition = this.width/2 + 5; - nameInput.yPosition = 45; - - guiFileChooser.xPosition = this.width/2 + 5; - guiFileChooser.yPosition = nameInput.yPosition + 20 + 5; - - xInput.yPosition = yInput.yPosition = zInput.yPosition = guiFileChooser.yPosition + 20 + 5 + 15; - yawInput.yPosition = pitchInput.yPosition = rollInput.yPosition = xInput.yPosition + 20 + 5 + 15; - scaleInput.yPosition = opacityInput.yPosition = yawInput.yPosition + 20 + 5 + 15; - - xInput.xPosition = scaleInput.xPosition = pitchInput.xPosition = this.width/2 + 8; - yInput.xPosition = yawInput.xPosition = xInput.xPosition + 50; - zInput.xPosition = rollInput.xPosition = yInput.xPosition + 50; - opacityInput.xPosition = scaleInput.xPosition + 75; - - xInput.width = yInput.width = zInput.width = yawInput.width = pitchInput.width = rollInput.width = 43; - scaleInput.width = opacityInput.width = 68; - - buttonList.add(removeButton); - buttonList.add(addButton); - buttonList.add(guiFileChooser); - - initialized = true; - } - - @Override - public void drawScreen(int mouseX, int mouseY, float partialTicks) { - this.drawCenteredString(fontRendererObj, screenTitle, this.width / 2, 5, Color.WHITE.getRGB()); - - int leftBorder = 10; - int topBorder = 20; - - drawGradientRect(leftBorder, topBorder, width - leftBorder, this.height - 10, -1072689136, -804253680); - - for(GuiTextField textField : textFields) { - textField.drawTextBox(); - } - - drawCenteredString(fontRendererObj, "X", xInput.xPosition + (xInput.width/2), xInput.yPosition - 12, Color.WHITE.getRGB()); - drawCenteredString(fontRendererObj, "Y", yInput.xPosition + (yInput.width/2), yInput.yPosition - 12, Color.WHITE.getRGB()); - drawCenteredString(fontRendererObj, "Z", zInput.xPosition + (zInput.width/2), zInput.yPosition - 12, Color.WHITE.getRGB()); - - drawCenteredString(fontRendererObj, "Yaw", yawInput.xPosition + (yawInput.width/2), yawInput.yPosition - 12, Color.WHITE.getRGB()); - drawCenteredString(fontRendererObj, "Pitch", pitchInput.xPosition + (pitchInput.width/2), pitchInput.yPosition - 12, Color.WHITE.getRGB()); - drawCenteredString(fontRendererObj, "Roll", rollInput.xPosition + (rollInput.width/2), rollInput.yPosition - 12, Color.WHITE.getRGB()); - - drawCenteredString(fontRendererObj, "Scale", scaleInput.xPosition + (scaleInput.width/2), scaleInput.yPosition - 12, Color.WHITE.getRGB()); - drawCenteredString(fontRendererObj, "Opacity", opacityInput.xPosition + (opacityInput.width/2), opacityInput.yPosition - 12, Color.WHITE.getRGB()); - super.drawScreen(mouseX, mouseY, partialTicks); - } - - @Override - public void updateScreen() { - for(GuiTextField textField : textFields) { - textField.updateCursorCounter(); - } - } - - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { - for(GuiTextField textField : textFields) { - textField.mouseClicked(mouseX, mouseY, mouseButton); - } - super.mouseClicked(mouseX, mouseY, mouseButton); - } - - @Override - protected void keyTyped(char typedChar, int keyCode) throws IOException { - for(GuiTextField textField : textFields) { - textField.textboxKeyTyped(typedChar, keyCode); - } - super.keyTyped(typedChar, keyCode); - - if(guiFileChooser.enabled) { - CustomImageObject current = objectList.getElement(objectList.getSelectionIndex()); - if(current == null) return; - - current.setName(nameInput.getText().trim()); - current.getPosition().setX(xInput.getPreciseValue()); - current.getPosition().setY(yInput.getPreciseValue()); - current.getPosition().setZ(zInput.getPreciseValue()); - current.getPosition().setPitch((float) pitchInput.getPreciseValue()); - current.getPosition().setYaw((float) yawInput.getPreciseValue()); - current.getPosition().setRoll((float) rollInput.getPreciseValue()); - current.getPosition().setScale((float) scaleInput.getPreciseValue() / 100f); - current.getPosition().setOpacity((float) opacityInput.getPreciseValue() / 100f); - } - - } - - @Override - public void onGuiClosed() { - ReplayHandler.setCustomImageObjects(objectList.getCopyOfElements()); - } - - @Override - protected void actionPerformed(GuiButton button) throws IOException { - if(!button.enabled) return; - - if(button.id == GuiConstants.ASSET_ADDER_REMOVE_BUTTON) { - CustomImageObject current = objectList.getElement(objectList.getSelectionIndex()); - if(current == null) return; - - objectList.removeElement(objectList.getSelectionIndex()); - } - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java index 8fa5101b..149b1b1e 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiEditKeyframe.java @@ -1,11 +1,12 @@ package eu.crushedpixel.replaymod.gui; import eu.crushedpixel.replaymod.ReplayMod; -import eu.crushedpixel.replaymod.events.KeyframesModifyEvent; -import eu.crushedpixel.replaymod.gui.elements.GuiAdvancedTextField; import eu.crushedpixel.replaymod.gui.elements.GuiArrowButton; import eu.crushedpixel.replaymod.gui.elements.GuiNumberInput; -import eu.crushedpixel.replaymod.holders.*; +import eu.crushedpixel.replaymod.holders.Keyframe; +import eu.crushedpixel.replaymod.holders.Position; +import eu.crushedpixel.replaymod.holders.TimestampValue; +import eu.crushedpixel.replaymod.interpolation.KeyframeList; import eu.crushedpixel.replaymod.replay.ReplayHandler; import eu.crushedpixel.replaymod.utils.RoundUtils; import eu.crushedpixel.replaymod.utils.TimestampUtils; @@ -13,7 +14,6 @@ import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.resources.I18n; -import net.minecraftforge.fml.common.FMLCommonHandler; import org.lwjgl.input.Keyboard; import java.awt.*; @@ -31,8 +31,6 @@ public class GuiEditKeyframe extends GuiScreen { private GuiNumberInput xCoord, yCoord, zCoord, pitch, yaw, roll; private GuiNumberInput min, sec, ms; - private GuiAdvancedTextField markerNameInput; - private List inputs = new ArrayList(); private List posInputs = new ArrayList(); @@ -43,7 +41,6 @@ public class GuiEditKeyframe extends GuiScreen { private Keyframe keyframeBackup; private boolean save; private boolean posKeyframe; - private boolean markerKeyframe; private Keyframe previous, next; @@ -57,27 +54,24 @@ public class GuiEditKeyframe extends GuiScreen { public GuiEditKeyframe(Keyframe keyframe) { this.keyframe = keyframe; this.keyframeBackup = keyframe.copy(); - this.posKeyframe = keyframe instanceof PositionKeyframe; - boolean timeKeyframe = keyframe instanceof TimeKeyframe; - this.markerKeyframe = keyframe instanceof MarkerKeyframe; + this.posKeyframe = keyframe.getValue() instanceof Position; + boolean timeKeyframe = keyframe.getValue() instanceof TimestampValue; ReplayHandler.selectKeyframe(null); + KeyframeList> positionKeyframes = ReplayHandler.getPositionKeyframes(); + KeyframeList> timeKeyframes = ReplayHandler.getTimeKeyframes(); + if(posKeyframe) { - previous = ReplayHandler.getPreviousPositionKeyframe(keyframe.getRealTimestamp() - 1); - next = ReplayHandler.getNextPositionKeyframe(keyframe.getRealTimestamp() + 1); + previous = positionKeyframes.getPreviousKeyframe(keyframe.getRealTimestamp() - 1); + next = positionKeyframes.getNextKeyframe(keyframe.getRealTimestamp() + 1); screenTitle = I18n.format("replaymod.gui.editkeyframe.title.pos"); } else if(timeKeyframe) { - previous = ReplayHandler.getPreviousTimeKeyframe(keyframe.getRealTimestamp() - 1); - next = ReplayHandler.getNextTimeKeyframe(keyframe.getRealTimestamp() + 1); + previous = timeKeyframes.getPreviousKeyframe(keyframe.getRealTimestamp() - 1); + next = timeKeyframes.getNextKeyframe(keyframe.getRealTimestamp() + 1); screenTitle = I18n.format("replaymod.gui.editkeyframe.title.time"); - } else if(markerKeyframe) { - previous = ReplayHandler.getPreviousMarkerKeyframe(keyframe.getRealTimestamp() - 1); - next = ReplayHandler.getNextMarkerKeyframe(keyframe.getRealTimestamp() + 1); - - screenTitle = I18n.format("replaymod.gui.editkeyframe.title.marker"); } ReplayHandler.selectKeyframe(keyframe); @@ -113,7 +107,7 @@ public class GuiEditKeyframe extends GuiScreen { //Position/Virtual Time Input if(posKeyframe) { - Position pos = ((PositionKeyframe)keyframe).getPosition(); + Position pos = ((Keyframe)keyframe).getValue(); xCoord = new GuiNumberInput(GuiConstants.KEYFRAME_EDITOR_X_INPUT, fontRendererObj, 0, 0, 100, null, null, RoundUtils.round(pos.getX()), true); yCoord = new GuiNumberInput(GuiConstants.KEYFRAME_EDITOR_Y_INPUT, fontRendererObj, 0, 0, 100, null, null, RoundUtils.round(pos.getY()), true); zCoord = new GuiNumberInput(GuiConstants.KEYFRAME_EDITOR_Z_INPUT, fontRendererObj, 0, 0, 100, null, null, RoundUtils.round(pos.getZ()), true); @@ -129,11 +123,6 @@ public class GuiEditKeyframe extends GuiScreen { posInputs.add(roll); inputs.addAll(posInputs); - } else if(markerKeyframe) { - markerNameInput = new GuiAdvancedTextField(fontRendererObj, 0, 0, 300, 20); - markerNameInput.setText(((MarkerKeyframe)keyframe).getName() == null ? "" : ((MarkerKeyframe)keyframe).getName()); - inputs.add(markerNameInput); - markerNameInput.hint = I18n.format("replaymod.gui.editkeyframe.markername"); } } @@ -170,9 +159,6 @@ public class GuiEditKeyframe extends GuiScreen { input.yPosition = i < 3 ? virtualY + 20 + i*30 : virtualY + 20 + (i-3)*30; i++; } - } else if(markerKeyframe) { - markerNameInput.xPosition = (width-markerNameInput.width)/2; - markerNameInput.yPosition = virtualY + (virtualHeight-markerNameInput.height)/2; } saveButton.yPosition = cancelButton.yPosition = virtualY + virtualHeight - 20 - 5; @@ -204,14 +190,12 @@ public class GuiEditKeyframe extends GuiScreen { } else { keyframe.setRealTimestamp(TimestampUtils.calculateTimestamp(min.getIntValue(), sec.getIntValue(), ms.getIntValue())); if(posKeyframe) { - ((PositionKeyframe)keyframe).setPosition(new Position(xCoord.getPreciseValue(), yCoord.getPreciseValue(), - zCoord.getPreciseValue(), new Float(pitch.getPreciseValue()), (float)yaw.getPreciseValue(), - (float)roll.getPreciseValue())); - } else if(markerKeyframe) { - ((MarkerKeyframe)keyframe).setName(markerNameInput.getText().trim()); + ((Keyframe)keyframe).setValue(new Position(xCoord.getPreciseValue(), yCoord.getPreciseValue(), + zCoord.getPreciseValue(), new Float(pitch.getPreciseValue()), (float) yaw.getPreciseValue(), + (float) roll.getPreciseValue(), null)); } } - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(ReplayHandler.getKeyframes())); + ReplayHandler.fireKeyframesModifyEvent(); Keyboard.enableRepeatEvents(false); } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiKeyframeRepository.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiKeyframeRepository.java index 12c14cf4..fb1a1c67 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiKeyframeRepository.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiKeyframeRepository.java @@ -6,7 +6,6 @@ import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener; import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay; import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.KeyframeSet; -import eu.crushedpixel.replaymod.holders.MarkerKeyframe; import eu.crushedpixel.replaymod.replay.ReplayHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; @@ -132,12 +131,9 @@ public class GuiKeyframeRepository extends GuiScreen implements GuiReplayOverlay if(!button.enabled) return; switch(button.id) { case GuiConstants.KEYFRAME_REPOSITORY_ADD_BUTTON: - List kfs = new ArrayList(ReplayHandler.getKeyframes()); - for(Keyframe kf : new ArrayList(kfs)) { - if(kf instanceof MarkerKeyframe) kfs.remove(kf); - } + List kfs = new ArrayList(ReplayHandler.getAllKeyframes()); - Keyframe[] keyframes = kfs.toArray(new Keyframe[ReplayHandler.getKeyframes().size()]); + Keyframe[] keyframes = kfs.toArray(new Keyframe[ReplayHandler.getAllKeyframes().size()]); KeyframeSet newSet = new KeyframeSet(I18n.format("replaymod.gui.keyframerepository.preset.defaultname"), keyframes); if(newSet.getPositionKeyframeCount() < 2 || newSet.getTimeKeyframeCount() < 1) { message = I18n.format("replaymod.chat.morekeyframes"); diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiKeyframeTimeline.java b/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiKeyframeTimeline.java index d4fe106b..6e0150ed 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiKeyframeTimeline.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiKeyframeTimeline.java @@ -1,18 +1,12 @@ package eu.crushedpixel.replaymod.gui.elements; -import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.gui.GuiEditKeyframe; import eu.crushedpixel.replaymod.holders.Keyframe; -import eu.crushedpixel.replaymod.holders.MarkerKeyframe; -import eu.crushedpixel.replaymod.holders.PositionKeyframe; -import eu.crushedpixel.replaymod.holders.TimeKeyframe; +import eu.crushedpixel.replaymod.holders.Position; +import eu.crushedpixel.replaymod.holders.TimestampValue; import eu.crushedpixel.replaymod.replay.ReplayHandler; -import eu.crushedpixel.replaymod.utils.MouseUtils; import net.minecraft.client.Minecraft; -import net.minecraft.client.resources.I18n; -import org.lwjgl.util.Point; -import java.awt.*; import java.util.ListIterator; public class GuiKeyframeTimeline extends GuiTimeline { @@ -22,21 +16,17 @@ public class GuiKeyframeTimeline extends GuiTimeline { private static final int KEYFRAME_TIME_Y = 25; private static final int KEYFRAME_SPEC_X = 74; private static final int KEYFRAME_SPEC_Y = 30; - private static final int KEYFRAME_MARKER_X = 109; - private static final int KEYFRAME_MARKER_Y = 20; private Keyframe clickedKeyFrame; private long clickTime; private boolean dragging; - private boolean markerKeyframes; private boolean timeKeyframes; private boolean placeKeyframes; public GuiKeyframeTimeline(int positionX, int positionY, int width, boolean showMarkers, - boolean showMarkerKeyframes, boolean showPlaceKeyframes, boolean showTimeKeyframes) { + boolean showPlaceKeyframes, boolean showTimeKeyframes) { super(positionX, positionY, width); this.showMarkers = showMarkers; - this.markerKeyframes = showMarkerKeyframes; this.timeKeyframes = showTimeKeyframes; this.placeKeyframes = showPlaceKeyframes; } @@ -55,11 +45,9 @@ public class GuiKeyframeTimeline extends GuiTimeline { Keyframe closest; if(mouseY >= positionY + BORDER_TOP + 5 && timeKeyframes) { - closest = ReplayHandler.getClosestTimeKeyframeForRealTime((int) time, tolerance); + closest = ReplayHandler.getTimeKeyframes().getClosestKeyframeForTimestamp((int) time, tolerance); } else if(mouseY >= positionY + BORDER_TOP && placeKeyframes) { - closest = ReplayHandler.getClosestPlaceKeyframeForRealTime((int) time, tolerance); - } else if(mouseY >= positionY + BORDER_TOP + 10 && markerKeyframes) { - closest = ReplayHandler.getClosestMarkerForRealTime((int) time, tolerance); + closest = ReplayHandler.getPositionKeyframes().getClosestKeyframeForTimestamp((int) time, tolerance); } else { closest = null; } @@ -84,26 +72,6 @@ public class GuiKeyframeTimeline extends GuiTimeline { } this.clickTime = currentTime; - } else if(button == 1) { - if(markerKeyframes) { - long time = getTimeAt(mouseX, mouseY); - if(time == -1) { - return; - } - - int tolerance = (int) (2 * Math.round(zoom * timelineLength / width)); - - MarkerKeyframe closest = null; - if(mouseY >= positionY + BORDER_TOP + 10 && markerKeyframes) { - closest = ReplayHandler.getClosestMarkerForRealTime((int) time, tolerance); - } - - if(closest != null) { - //Jump to clicked Marker Keyframe - ReplayHandler.setLastPosition(closest.getPosition()); - ReplayMod.replaySender.jumpToTime(closest.getRealTimestamp()); - } - } } } @@ -117,7 +85,8 @@ public class GuiKeyframeTimeline extends GuiTimeline { if (dragging || Math.abs(clickedKeyFrame.getRealTimestamp() - time) > tolerance) { clickedKeyFrame.setRealTimestamp((int) time); - ReplayHandler.sortKeyframes(); + ReplayHandler.getPositionKeyframes().sort(); + ReplayHandler.getTimeKeyframes().sort(); dragging = true; } } else if (dragging && placeKeyframes) { @@ -146,22 +115,22 @@ public class GuiKeyframeTimeline extends GuiTimeline { //iterate over keyframes to find spectator segments if(placeKeyframes) { - ListIterator iterator = ReplayHandler.getKeyframes().listIterator(); + ListIterator> iterator = ReplayHandler.getPositionKeyframes().listIterator(); while(iterator.hasNext()) { - Keyframe kf = iterator.next(); + Keyframe kf = iterator.next(); - if(!(kf instanceof PositionKeyframe) || ((PositionKeyframe) kf).getSpectatedEntityID() == null) + if(kf.getValue().getSpectatedEntityID() == null) continue; int i = iterator.nextIndex(); int nextSpectatorKeyframeRealTime = -1; while(iterator.hasNext()) { - Keyframe kf2 = iterator.next(); + Keyframe kf2 = iterator.next(); - if(kf2 instanceof PositionKeyframe) { - if(((PositionKeyframe) kf).getSpectatedEntityID() - .equals(((PositionKeyframe) kf2).getSpectatedEntityID())) { + if(kf2.getValue() instanceof Position) { + if(kf.getValue().getSpectatedEntityID() + .equals(kf2.getValue().getSpectatedEntityID())) { nextSpectatorKeyframeRealTime = kf2.getRealTimestamp(); } @@ -189,7 +158,7 @@ public class GuiKeyframeTimeline extends GuiTimeline { //Draw Keyframe logos - for (Keyframe kf : ReplayHandler.getKeyframes()) { + for (Keyframe kf : ReplayHandler.getAllKeyframes()) { if (kf != null && !kf.equals(ReplayHandler.getSelectedKeyframe())) drawKeyframe(kf, bodyWidth, leftTime, rightTime, segmentLength); } @@ -214,21 +183,6 @@ public class GuiKeyframeTimeline extends GuiTimeline { long leftTime = Math.round(timeStart * timelineLength); double segmentLength = timelineLength * zoom; - if(markerKeyframes) { - for(MarkerKeyframe marker : ReplayHandler.getMarkers()) { - int keyframeX = getKeyframeX(marker.getRealTimestamp(), leftTime, bodyWidth, segmentLength); - - if(MouseUtils.isMouseWithinBounds(keyframeX - 2, this.positionY + BORDER_TOP + 10 + 1, 5, 5)) { - Point mouse = MouseUtils.getMousePos(); - String markerName = marker.getName(); - if(markerName == null) markerName = I18n.format("replaymod.gui.ingame.unnamedmarker"); - ReplayMod.tooltipRenderer.drawTooltip(mouse.getX(), mouse.getY(), markerName, null, Color.WHITE); - - drawn = true; - } - } - } - if(!drawn) { super.drawOverlay(mc, mouseX, mouseY); } @@ -242,27 +196,22 @@ public class GuiKeyframeTimeline extends GuiTimeline { int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, bodyWidth, segmentLength); - if(kf instanceof PositionKeyframe) { + if(kf.getValue() instanceof Position) { if(!placeKeyframes) return; textureX = KEYFRAME_PLACE_X; textureY = KEYFRAME_PLACE_Y; y += 0; //If Spectator Keyframe, use different texture - if(((PositionKeyframe) kf).getSpectatedEntityID() != null) { + if(((Keyframe) kf).getValue().getSpectatedEntityID() != null) { textureX = KEYFRAME_SPEC_X; textureY = KEYFRAME_SPEC_Y; } - } else if(kf instanceof TimeKeyframe) { + } else if(kf.getValue() instanceof TimestampValue) { if(!timeKeyframes) return; textureX = KEYFRAME_TIME_X; textureY = KEYFRAME_TIME_Y; y += 5; - } else if(kf instanceof MarkerKeyframe) { - if(!markerKeyframes) return; - textureX = KEYFRAME_MARKER_X; - textureY = KEYFRAME_MARKER_Y; - y += 10; } else { throw new UnsupportedOperationException("Unknown keyframe type: " + kf.getClass()); } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java b/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java index ce379cff..1eac1617 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java @@ -7,10 +7,9 @@ import eu.crushedpixel.replaymod.gui.GuiMouseInput; import eu.crushedpixel.replaymod.gui.GuiRenderSettings; import eu.crushedpixel.replaymod.gui.GuiReplaySpeedSlider; import eu.crushedpixel.replaymod.gui.elements.*; -import eu.crushedpixel.replaymod.holders.MarkerKeyframe; +import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.Position; -import eu.crushedpixel.replaymod.holders.PositionKeyframe; -import eu.crushedpixel.replaymod.holders.TimeKeyframe; +import eu.crushedpixel.replaymod.holders.TimestampValue; import eu.crushedpixel.replaymod.registry.ReplayGuiRegistry; import eu.crushedpixel.replaymod.replay.ReplayHandler; import eu.crushedpixel.replaymod.utils.MouseUtils; @@ -98,7 +97,7 @@ public class GuiReplayOverlay extends Gui { @Override public void run() { //if not enough keyframes, abort and leave chat message - if(ReplayHandler.getPosKeyframeCount() < 2 || ReplayHandler.getTimeKeyframeCount() < 1) { + if(ReplayHandler.getPositionKeyframes().size() < 2 || ReplayHandler.getTimeKeyframes().size() < 1) { ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.morekeyframes", ChatMessageHandler.ChatMessageType.WARNING); } else { mc.displayGuiScreen(new GuiRenderSettings()); @@ -137,12 +136,12 @@ public class GuiReplayOverlay extends Gui { Entity cam = mc.getRenderViewEntity(); if (cam != null) { Position position = new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, - cam.rotationYaw % 360, ReplayHandler.getCameraTilt()); + cam.rotationYaw % 360, ReplayHandler.getCameraTilt(), null); if (ReplayHandler.isCamera()) - ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), position)); + ReplayHandler.addKeyframe(new Keyframe(ReplayHandler.getRealTimelineCursor(), position)); else - ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), cam.getEntityId())); + ReplayHandler.addKeyframe(new Keyframe(ReplayHandler.getRealTimelineCursor(), new Position(cam.getEntityId(), true))); } } }, "replaymod.gui.ingame.menu.addposkeyframe"); @@ -160,12 +159,12 @@ public class GuiReplayOverlay extends Gui { Entity cam = mc.getRenderViewEntity(); if (cam != null) { Position position = new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, - cam.rotationYaw % 360, ReplayHandler.getCameraTilt()); + cam.rotationYaw % 360, ReplayHandler.getCameraTilt(), null); if (ReplayHandler.isCamera()) - ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), position)); + ReplayHandler.addKeyframe(new Keyframe(ReplayHandler.getRealTimelineCursor(), position)); else - ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), cam.getEntityId())); + ReplayHandler.addKeyframe(new Keyframe(ReplayHandler.getRealTimelineCursor(), new Position(cam.getEntityId(), true))); } } }, "replaymod.gui.ingame.menu.addspeckeyframe"); @@ -179,10 +178,10 @@ public class GuiReplayOverlay extends Gui { @Override public GuiElement delegate() { - boolean selected = ReplayHandler.getSelectedKeyframe() instanceof PositionKeyframe; + boolean selected = ReplayHandler.getSelectedKeyframe() != null && ReplayHandler.getSelectedKeyframe().getValue() instanceof Position; boolean camera; if(selected) { - camera = ((PositionKeyframe)ReplayHandler.getSelectedKeyframe()).getSpectatedEntityID() == null; + camera = ((Keyframe)ReplayHandler.getSelectedKeyframe()).getValue().getSpectatedEntityID() == null; } else { camera = ReplayHandler.isCamera(); } @@ -200,7 +199,7 @@ public class GuiReplayOverlay extends Gui { private final GuiElement buttonNotSelected = texturedButton(BUTTON_TIME_X, BOTTOM_ROW, 0, 80, 20, new Runnable() { @Override public void run() { - ReplayHandler.addKeyframe(new TimeKeyframe(ReplayHandler.getRealTimelineCursor(), ReplayMod.replaySender.currentTimeStamp())); + ReplayHandler.addKeyframe(new Keyframe(ReplayHandler.getRealTimelineCursor(), new TimestampValue(ReplayMod.replaySender.currentTimeStamp()))); } }, "replaymod.gui.ingame.menu.addtimekeyframe"); @@ -213,7 +212,7 @@ public class GuiReplayOverlay extends Gui { @Override public GuiElement delegate() { - return ReplayHandler.getSelectedKeyframe() instanceof TimeKeyframe ? buttonSelected : buttonNotSelected; + return ReplayHandler.getSelectedKeyframe() != null && ReplayHandler.getSelectedKeyframe().getValue() instanceof TimestampValue ? buttonSelected : buttonNotSelected; } }; @@ -233,17 +232,12 @@ public class GuiReplayOverlay extends Gui { } }, "replaymod.gui.ingame.menu.zoomout"); - private final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(TIMELINE_X, TOP_ROW - 1, WIDTH - 14 - TIMELINE_X, false, true, false, false) { - @Override - public void mouseClick(Minecraft mc, int mouseX, int mouseY, int button) { - if(!enabled) return; - super.mouseClick(mc, mouseX, mouseY, button); - if(!(ReplayHandler.getSelectedKeyframe() instanceof MarkerKeyframe)) - performJump(timeline.getTimeAt(mouseX, mouseY)); - } + //TODO: GuiMarkerKeyframeTimeline + private final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(TIMELINE_X, TOP_ROW - 1, WIDTH - 14 - TIMELINE_X, false, false, false) { + }; - private final GuiKeyframeTimeline timelineReal = new GuiKeyframeTimeline(TIMELINE_REAL_X, BOTTOM_ROW - 1, TIMELINE_REAL_WIDTH, true, false, true, true); + private final GuiKeyframeTimeline timelineReal = new GuiKeyframeTimeline(TIMELINE_REAL_X, BOTTOM_ROW - 1, TIMELINE_REAL_WIDTH, true, true, true); { timelineReal.timelineLength = 10 * 60 * 1000; } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/ExtendedPosition.java b/src/main/java/eu/crushedpixel/replaymod/holders/ExtendedPosition.java deleted file mode 100644 index 7a580174..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/holders/ExtendedPosition.java +++ /dev/null @@ -1,21 +0,0 @@ -package eu.crushedpixel.replaymod.holders; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -public class ExtendedPosition extends Position { - - public ExtendedPosition(double x, double y, double z, float width, float height) { - super(x, y, z, 0, 0); - this.width = width; - this.height = height; - } - - private float anchorX, anchorY; - private float width, height; - private float scale = 1f; - private float opacity = 1f; - -} diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java index c440d4e6..e99dc962 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Keyframe.java @@ -1,5 +1,6 @@ package eu.crushedpixel.replaymod.holders; +import eu.crushedpixel.replaymod.interpolation.KeyframeValue; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -7,9 +8,12 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode @AllArgsConstructor -public abstract class Keyframe { +public class Keyframe { private int realTimestamp; + private T value; - public abstract Keyframe copy(); + public Keyframe copy() { + return new Keyframe(realTimestamp, value); + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/KeyframeSet.java b/src/main/java/eu/crushedpixel/replaymod/holders/KeyframeSet.java index 65dd8911..43a72dcd 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/KeyframeSet.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/KeyframeSet.java @@ -8,8 +8,8 @@ import java.util.List; public class KeyframeSet implements GuiEntryListEntry { private String name; - private PositionKeyframe[] positionKeyframes; - private TimeKeyframe[] timeKeyframes; + private Keyframe[] positionKeyframes; + private Keyframe[] timeKeyframes; public KeyframeSet(String name, Keyframe[] keyframes) { this.name = name; @@ -21,18 +21,18 @@ public class KeyframeSet implements GuiEntryListEntry { } public void setKeyframes(Keyframe[] keyframes) { - List posKFList = new ArrayList(); - List timeKFList = new ArrayList(); + List> posKFList = new ArrayList>(); + List> timeKFList = new ArrayList>(); for(Keyframe kf : keyframes) { - if(kf instanceof PositionKeyframe) - posKFList.add((PositionKeyframe)kf); - else if(kf instanceof TimeKeyframe) - timeKFList.add((TimeKeyframe) kf); + if(kf.getValue() instanceof Position) + posKFList.add((Keyframe)kf); + else if(kf.getValue() instanceof TimestampValue) + timeKFList.add((Keyframe) kf); } - positionKeyframes = posKFList.toArray(new PositionKeyframe[posKFList.size()]); - timeKeyframes = timeKFList.toArray(new TimeKeyframe[timeKFList.size()]); + positionKeyframes = posKFList.toArray(new Keyframe[posKFList.size()]); + timeKeyframes = timeKFList.toArray(new Keyframe[timeKFList.size()]); } public String getName() { diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/MarkerKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/MarkerKeyframe.java index 67191be2..654fd65a 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/MarkerKeyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/MarkerKeyframe.java @@ -1,23 +1,14 @@ package eu.crushedpixel.replaymod.holders; +import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; @Data -@EqualsAndHashCode(callSuper = true) -public final class MarkerKeyframe extends Keyframe { +@AllArgsConstructor +public final class MarkerKeyframe { + private int realTimestamp; private Position position; private String name; - public MarkerKeyframe(Position position, int timestamp, String name) { - super(timestamp); - this.position = position; - this.name = name; - } - - @Override - public Keyframe copy() { - return new MarkerKeyframe(position, getRealTimestamp(), name); - } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Point.java b/src/main/java/eu/crushedpixel/replaymod/holders/Point.java new file mode 100644 index 00000000..81bb183a --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Point.java @@ -0,0 +1,13 @@ +package eu.crushedpixel.replaymod.holders; + +import eu.crushedpixel.replaymod.interpolation.KeyframeValue; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +public class Point extends KeyframeValue { + + public double x, y; + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java index 16ce25e8..12a198be 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java @@ -1,27 +1,38 @@ package eu.crushedpixel.replaymod.holders; +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; @Data @AllArgsConstructor -public class Position { +@NoArgsConstructor +@EqualsAndHashCode(callSuper=true) +public class Position extends KeyframeValue { - private double x, y, z; - private float pitch, yaw, roll; + public double x, y, z; + public double pitch, yaw, roll; - public Position(int entityID) { - this(Minecraft.getMinecraft().theWorld.getEntityByID(entityID)); + private Integer spectatedEntityID; + + public Position(int entityID, boolean spectate) { + this(Minecraft.getMinecraft().theWorld.getEntityByID(entityID), spectate); } - public Position(Entity e) { - this(e.posX, e.posY, e.posZ, e.rotationPitch, e.rotationYaw); + public Position(Entity e, boolean spectate) { + this(e.posX, e.posY, e.posZ, e.rotationPitch, e.rotationYaw, spectate ? e.getEntityId() : null); + } + + public Position(double x, double y, double z, float pitch, float yaw, Integer spectatedEntityID) { + this(x, y, z, pitch, yaw, 0, spectatedEntityID); } public Position(double x, double y, double z, float pitch, float yaw) { - this(x, y, z, pitch, yaw, 0); + this(x, y, z, pitch, yaw, 0, null); } public double distanceSquared(double x, double y, double z) { diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java deleted file mode 100755 index 9862bddf..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java +++ /dev/null @@ -1,31 +0,0 @@ -package eu.crushedpixel.replaymod.holders; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -public final class PositionKeyframe extends Keyframe { - - private Position position; - private Integer spectatedEntityID; - - public PositionKeyframe(int realTime, Position position) { - this(realTime, position, null); - } - - public PositionKeyframe(int realTime, Position position, Integer spectatedEntityID) { - super(realTime); - this.position = position; - this.spectatedEntityID = spectatedEntityID; - } - - public PositionKeyframe(int realTime, int spectatedEntityID) { - this(realTime, new Position(spectatedEntityID), spectatedEntityID); - } - - @Override - public Keyframe copy() { - return new PositionKeyframe(getRealTimestamp(), position, spectatedEntityID); - } -} \ No newline at end of file diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/TimeKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/TimeKeyframe.java deleted file mode 100755 index 68e37806..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/holders/TimeKeyframe.java +++ /dev/null @@ -1,21 +0,0 @@ -package eu.crushedpixel.replaymod.holders; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -public final class TimeKeyframe extends Keyframe { - - private final int timestamp; - - public TimeKeyframe(int realTime, int timestamp) { - super(realTime); - this.timestamp = timestamp; - } - - @Override - public Keyframe copy() { - return new TimeKeyframe(getRealTimestamp(), getTimestamp()); - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java b/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java new file mode 100644 index 00000000..cd2c1360 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/holders/TimestampValue.java @@ -0,0 +1,13 @@ +package eu.crushedpixel.replaymod.holders; + +import eu.crushedpixel.replaymod.interpolation.KeyframeValue; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +public class TimestampValue extends KeyframeValue { + + public double value; + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java b/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java new file mode 100644 index 00000000..b4bb509a --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java @@ -0,0 +1,20 @@ +package eu.crushedpixel.replaymod.holders; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.awt.*; + +@Data +@AllArgsConstructor +public class Transformations { + + private Position anchor; + private Position position; + private Position orientation; + private Point scale; + private float opacity; + + private float width, height; + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericLinearInterpolation.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericLinearInterpolation.java new file mode 100755 index 00000000..06f96ece --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericLinearInterpolation.java @@ -0,0 +1,60 @@ +package eu.crushedpixel.replaymod.interpolation; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class GenericLinearInterpolation implements Interpolation { + + private Field[] fields; + + protected List points = new ArrayList(); + + public GenericLinearInterpolation() { + points = new ArrayList(); + } + + @Override + public void prepare() {} + + public void addPoint(T point) { + points.add(point); + + List fields = new ArrayList(); + for(Field f : point.getClass().getDeclaredFields()) { + if(Modifier.isPublic(f.getModifiers())) { + fields.add(f); + } + } + this.fields = fields.toArray(new Field[fields.size()]); + } + + @Override + public void applyPoint(float position, T toEdit) { + if(points.isEmpty()) { + throw new IllegalStateException("At least one Value needs to be added for this operation"); + } + + //first, get previous and next T for given position + float relative = position * (points.size()-1); + int previousIndex = (int)Math.floor(relative); + int nextIndex = (int)Math.ceil(relative); + float percentage = relative - previousIndex; + + T previous = points.get(previousIndex); + T next = points.get(nextIndex); + + for(Field f : fields) { + try { + f.set(toEdit, getInterpolatedValue(f.getDouble(previous), f.getDouble(next), percentage)); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + private double getInterpolatedValue(double val1, double val2, float perc) { + return val1 + ((val2 - val1) * perc); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericSplineInterpolation.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericSplineInterpolation.java new file mode 100755 index 00000000..d6f75842 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/GenericSplineInterpolation.java @@ -0,0 +1,75 @@ +package eu.crushedpixel.replaymod.interpolation; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +public class GenericSplineInterpolation extends BasicSpline implements Interpolation { + + private Field[] fields; + + private Vector points; + private Vector[] cubics = new Vector[0]; + + public GenericSplineInterpolation() { + this.points = new Vector(); + } + + public void addPoint(T point) { + this.points.add(point); + + List fields = new ArrayList(); + for(Field f : point.getClass().getDeclaredFields()) { + if(Modifier.isPublic(f.getModifiers())) { + fields.add(f); + } + } + this.fields = fields.toArray(new Field[fields.size()]); + } + + public Vector getPoints() { + return points; + } + + @Override + public void prepare() { + if(fields.length <= 0) { + throw new IllegalStateException("The passed KeyframeValue class" + + " has to contain at least one Field"); + } + if(!points.isEmpty()) { + cubics = new Vector[fields.length]; + for(int i=0; i(); + try { + calcNaturalCubic(points, fields[i], cubics[i]); + } catch(Exception e) { + e.printStackTrace(); + } + } + } else { + throw new IllegalStateException("At least one Value needs to be added" + + " before preparing this Spline"); + } + } + + public void applyPoint(float position, T toEdit) { + Vector first = cubics[0]; + position = position * first.size(); + int cubicNum = (int) Math.min(cubics[0].size() - 1, position); + float cubicPos = (position - cubicNum); + + int i = 0; + for(Field f : fields) { + try { + f.set(toEdit, cubics[i].get(cubicNum).eval(cubicPos)); + } catch(Exception e) { + e.printStackTrace(); + } + i++; + } + } + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/Interpolation.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/Interpolation.java index 9c0a63b7..cb630060 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/Interpolation.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/Interpolation.java @@ -1,7 +1,7 @@ package eu.crushedpixel.replaymod.interpolation; -public interface Interpolation { +public interface Interpolation { void prepare(); - T getPoint(float position); + void applyPoint(float position, T toEdit); void addPoint(T pos); } diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java new file mode 100644 index 00000000..d68e85fb --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java @@ -0,0 +1,170 @@ +package eu.crushedpixel.replaymod.interpolation; + +import eu.crushedpixel.replaymod.holders.Keyframe; +import eu.crushedpixel.replaymod.holders.KeyframeComparator; +import eu.crushedpixel.replaymod.holders.Position; + +import java.util.ArrayList; +import java.util.List; + +public class KeyframeList extends ArrayList { + + private static final KeyframeComparator KEYFRAME_COMPARATOR = new KeyframeComparator(); + + @Override + public boolean add(T t) { + boolean success = super.add(t); + sort(); + return success; + } + + @Override + public void add(int index, T element) { + super.add(index, element); + sort(); + } + + @Override + public T remove(int index) { + T removed = super.remove(index); + sort(); + return removed; + } + + @Override + public boolean remove(Object o) { + boolean success = super.remove(o); + sort(); + return success; + } + + public void sort() { + sort(KEYFRAME_COMPARATOR); + } + + /** + * Returns the first Keyframe that comes before a given value. + * @param realTime The value to use + * @return The first Keyframe prior to the given value + */ + public T getPreviousKeyframe(int realTime) { + if(this.isEmpty()) return null; + + T backup = null; + + List found = new ArrayList(); + + for(T kf : this) { + + if(kf.getRealTimestamp() < realTime) { + found.add(kf); + } else if(kf.getRealTimestamp() == realTime) { + backup = kf; + } + + } + + if(found.size() > 0) + return found.get(found.size() - 1); //last element is nearest + + else return backup; + } + + /** + * Returns the first Keyframe that comes after a given value. + * @param realTime The value to use + * @return The first Keyframe after the given value + */ + public T getNextKeyframe(int realTime) { + if(this.isEmpty()) return null; + + T backup = null; + + for(T kf : this) { + + if(kf.getRealTimestamp() > realTime) { + return kf; //first found element is next + } else if(kf.getRealTimestamp() == realTime) { + backup = kf; + } + + } + + return backup; + } + + /** + * Returns the Keyframe that is closest to the given timestamp. + * @param realTime The timestamp to start searching at + * @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) { + if(!(kf.getValue() instanceof Position)) continue; + if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { + found.add(kf); + } + } + + T closest = null; + + for(T kf : found) { + if(closest == null || Math.abs(closest.getRealTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) { + closest = kf; + } + } + return closest; + } + + public T first() { + if(isEmpty()) return null; + return get(0); + } + + public T last() { + if(isEmpty()) return null; + return get(size()-1); + } + + /** + * 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) { + Keyframe previousKeyframe = getPreviousKeyframe(timestamp); + Keyframe nextKeyframe = getNextKeyframe(timestamp); + + int previousTimestamp = 0; + int nextTimestamp = 0; + + if(nextKeyframe != null || previousKeyframe != null) { + if(nextKeyframe != null) { + nextTimestamp = nextKeyframe.getRealTimestamp(); + } else { + nextTimestamp = previousKeyframe.getRealTimestamp(); + } + + if(previousKeyframe != null) { + previousTimestamp = previousKeyframe.getRealTimestamp(); + } else { + previousTimestamp = nextKeyframe.getRealTimestamp(); + } + } + + int currentPosDiff = nextTimestamp - previousTimestamp; + int currentPos = timestamp - previousTimestamp; + + float currentStepPercentage = (float) currentPos / (float) currentPosDiff; + if(Float.isInfinite(currentStepPercentage)) currentStepPercentage = 0; + + float value = (indexOf(previousKeyframe) + currentStepPercentage) / + (float)(size() - 1); + + return Math.max(0, Math.min(1, value)); + } + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java new file mode 100644 index 00000000..d6a31e1d --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeValue.java @@ -0,0 +1,12 @@ +package eu.crushedpixel.replaymod.interpolation; + +/** + * An abstract class that GenericSplineInterpolation can process. + * Subclasses simply have to declare at least one public double field, + * and the GenericSplineInterpolation will interpolate these. + *

+ * It is recommended for KeyframeValue subclasses to have a @NoArgsConstructor annotation. + */ +public abstract class KeyframeValue { + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearInterpolation.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearInterpolation.java deleted file mode 100755 index 2e2f884e..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearInterpolation.java +++ /dev/null @@ -1,47 +0,0 @@ -package eu.crushedpixel.replaymod.interpolation; - -import akka.japi.Pair; - -import java.util.ArrayList; -import java.util.List; - -public abstract class LinearInterpolation implements Interpolation { - - protected List points = new ArrayList(); - - public LinearInterpolation() { - points = new ArrayList(); - } - - @Override - public void prepare() { - - } - - public abstract K getPoint(float position); - - public void addPoint(K point) { - points.add(point); - } - - protected Pair> getCurrentPoints(float position) { - if(points.size() == 0) return null; - position = position * (points.size() - 1); - int cubicNum = (int) Math.min(points.size() - 1, position); - float cubicPos = (position - cubicNum); - - if(cubicNum == points.size() - 1) { - cubicNum--; - cubicPos++; - } - - if(cubicNum < 0) { - return new Pair>(cubicPos, new Pair(points.get(cubicNum + 1), points.get(cubicNum + 1))); - } - return new Pair>(cubicPos, new Pair(points.get(cubicNum), points.get(cubicNum + 1))); - } - - protected double getInterpolatedValue(double val1, double val2, float perc) { - return val1 + ((val2 - val1) * perc); - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearPoint.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearPoint.java deleted file mode 100755 index 053f8e46..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearPoint.java +++ /dev/null @@ -1,29 +0,0 @@ -package eu.crushedpixel.replaymod.interpolation; - -import akka.japi.Pair; -import eu.crushedpixel.replaymod.holders.Position; - -public class LinearPoint extends LinearInterpolation { - - @Override - public Position getPoint(float positionIn) { - Pair> pair = getCurrentPoints(positionIn); - if(pair == null) return null; - - float perc = pair.first(); - - Position first = pair.second().first(); - Position second = pair.second().second(); - - double x = getInterpolatedValue(first.getX(), second.getX(), perc); - double y = getInterpolatedValue(first.getY(), second.getY(), perc); - double z = getInterpolatedValue(first.getZ(), second.getZ(), perc); - - float pitch = (float)getInterpolatedValue(first.getPitch(), second.getPitch(), perc); - float yaw = (float)getInterpolatedValue(first.getYaw(), second.getYaw(), perc); - - float rot = (float)getInterpolatedValue(first.getRoll(), second.getRoll(), perc); - - return new Position(x, y, z, pitch, yaw, rot); - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearTimestamp.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearTimestamp.java deleted file mode 100755 index bcc2c2cf..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/LinearTimestamp.java +++ /dev/null @@ -1,19 +0,0 @@ -package eu.crushedpixel.replaymod.interpolation; - -import akka.japi.Pair; - -public class LinearTimestamp extends LinearInterpolation { - - @Override - public Integer getPoint(float position) { - Pair> pair = getCurrentPoints(position); - if(pair == null) return null; - - float perc = pair.first(); - - int first = pair.second().first(); - int second = pair.second().second(); - - return (int) getInterpolatedValue(first, second, perc); - } -} diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/SplinePoint.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/SplinePoint.java deleted file mode 100755 index d9f33c93..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/SplinePoint.java +++ /dev/null @@ -1,93 +0,0 @@ -package eu.crushedpixel.replaymod.interpolation; - -import eu.crushedpixel.replaymod.holders.Position; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.util.Vector; - -public class SplinePoint extends BasicSpline implements Interpolation { - private Vector points; - private Vector xCubics; - private Vector yCubics; - private Vector zCubics; - private Vector pitchCubics; - private Vector yawCubics; - private Vector rotCubics; - private Field vectorX; - private Field vectorY; - private Field vectorZ; - private Field vectorPitch; - private Field vectorYaw; - private Field vectorRot; - - public SplinePoint() { - this.points = new Vector(); - - this.xCubics = new Vector(); - this.yCubics = new Vector(); - this.zCubics = new Vector(); - this.pitchCubics = new Vector(); - this.yawCubics = new Vector(); - this.rotCubics = new Vector(); - - try { - vectorX = Position.class.getDeclaredField("x"); - vectorY = Position.class.getDeclaredField("y"); - vectorZ = Position.class.getDeclaredField("z"); - vectorPitch = Position.class.getDeclaredField("pitch"); - vectorYaw = Position.class.getDeclaredField("yaw"); - vectorRot = Position.class.getDeclaredField("roll"); - vectorX.setAccessible(true); - vectorY.setAccessible(true); - vectorZ.setAccessible(true); - vectorPitch.setAccessible(true); - vectorYaw.setAccessible(true); - vectorRot.setAccessible(true); - } catch(SecurityException e) { - e.printStackTrace(); - } catch(NoSuchFieldException e) { - e.printStackTrace(); - } - } - - public void addPoint(Position point) { - this.points.add(point); - } - - public Vector getPoints() { - return points; - } - - @Override - public void prepare() { - try { - calcNaturalCubic(points, vectorX, xCubics); - calcNaturalCubic(points, vectorY, yCubics); - calcNaturalCubic(points, vectorZ, zCubics); - calcNaturalCubic(points, vectorPitch, pitchCubics); - calcNaturalCubic(points, vectorYaw, yawCubics); - calcNaturalCubic(points, vectorRot, rotCubics); - } catch(IllegalArgumentException e) { - e.printStackTrace(); - } catch(IllegalAccessException e) { - e.printStackTrace(); - } catch(InvocationTargetException e) { - e.printStackTrace(); - } - } - - public Position getPoint(float position) { - position = position * xCubics.size(); - int cubicNum = (int) Math.min(xCubics.size() - 1, position); - float cubicPos = (position - cubicNum); - - return new Position(xCubics.get(cubicNum).eval(cubicPos), - yCubics.get(cubicNum).eval(cubicPos), - zCubics.get(cubicNum).eval(cubicPos), - (float) pitchCubics.get(cubicNum).eval(cubicPos), - (float) yawCubics.get(cubicNum).eval(cubicPos), - (float) rotCubics.get(cubicNum).eval(cubicPos)); - } - -} diff --git a/src/main/java/eu/crushedpixel/replaymod/recording/PacketListener.java b/src/main/java/eu/crushedpixel/replaymod/recording/PacketListener.java index 7ce601b8..7441d076 100755 --- a/src/main/java/eu/crushedpixel/replaymod/recording/PacketListener.java +++ b/src/main/java/eu/crushedpixel/replaymod/recording/PacketListener.java @@ -204,10 +204,10 @@ public class PacketListener extends DataListener { } public void addMarker() { - Position pos = new Position(Minecraft.getMinecraft().getRenderViewEntity()); + Position pos = new Position(Minecraft.getMinecraft().getRenderViewEntity(), false); int timestamp = (int) (System.currentTimeMillis() - startTime); - MarkerKeyframe marker = new MarkerKeyframe(pos, timestamp, null); + MarkerKeyframe marker = new MarkerKeyframe(timestamp, pos, null); markers.add(marker); diff --git a/src/main/java/eu/crushedpixel/replaymod/renderer/CustomObjectRenderer.java b/src/main/java/eu/crushedpixel/replaymod/renderer/CustomObjectRenderer.java index e00177cc..942c2289 100644 --- a/src/main/java/eu/crushedpixel/replaymod/renderer/CustomObjectRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/renderer/CustomObjectRenderer.java @@ -1,16 +1,12 @@ package eu.crushedpixel.replaymod.renderer; -import eu.crushedpixel.replaymod.holders.CustomImageObject; -import eu.crushedpixel.replaymod.holders.ExtendedPosition; +import eu.crushedpixel.replaymod.assets.CustomImageObject; import eu.crushedpixel.replaymod.replay.ReplayHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.entity.Entity; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import org.lwjgl.opengl.GL11; /** * Allows users to render custom images in the World. @@ -51,6 +47,8 @@ public class CustomObjectRenderer { } private void drawCustomImageObject(double playerX, double playerY, double playerZ, CustomImageObject customImageObject) { + //TODO: Get current Transformations for given Timestamp + /* GlStateManager.pushMatrix(); GlStateManager.enableTexture2D(); GlStateManager.enableLighting(); @@ -64,7 +62,7 @@ public class CustomObjectRenderer { mc.renderEngine.bindTexture(customImageObject.getResourceLocation()); - ExtendedPosition objectPosition = customImageObject.getPosition(); + Transformations objectPosition = customImageObject.getPosition(); double x = objectPosition.getX() - playerX; double y = objectPosition.getY() - playerY; @@ -111,5 +109,6 @@ public class CustomObjectRenderer { GlStateManager.disableTexture2D(); GlStateManager.enableLighting(); GlStateManager.popMatrix(); + */ } } diff --git a/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java b/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java index 3eb36977..91669b5f 100644 --- a/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/renderer/PathPreviewRenderer.java @@ -5,8 +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.holders.PositionKeyframe; -import eu.crushedpixel.replaymod.interpolation.SplinePoint; +import eu.crushedpixel.replaymod.interpolation.GenericSplineInterpolation; import eu.crushedpixel.replaymod.replay.ReplayHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; @@ -27,11 +26,11 @@ public class PathPreviewRenderer { private static final Minecraft mc = Minecraft.getMinecraft(); - private SplinePoint spline = new SplinePoint(); + private GenericSplineInterpolation spline = new GenericSplineInterpolation(); private DistanceComparator distanceComparator = new DistanceComparator(); - private List keyframes = new ArrayList(); + private List> keyframes = new ArrayList>(); @SubscribeEvent public void renderCameraPath(RenderWorldLastEvent event) { @@ -72,7 +71,8 @@ public class PathPreviewRenderer { float max = spline.getPoints().size() * 50; for(int i = 0; i < max; i++) { - Position point = spline.getPoint(i / max); + Position point = new Position(); + spline.applyPoint(i / max, point); if(prev != null) { drawConnection(doubleX, doubleY, doubleZ, prev, point, Color.RED.getRGB()); @@ -88,7 +88,7 @@ public class PathPreviewRenderer { Collections.sort(keyframes, distanceComparator); - for(PositionKeyframe kf : keyframes) { + for(Keyframe kf : keyframes) { drawPoint(doubleX, doubleY, doubleZ, kf); } @@ -102,15 +102,13 @@ public class PathPreviewRenderer { @SubscribeEvent public void recalcSpline(KeyframesModifyEvent event) { - keyframes = new ArrayList(); - spline = new SplinePoint(); - for(Keyframe kf : event.keyframes) { - if(kf instanceof PositionKeyframe) { - PositionKeyframe pkf = (PositionKeyframe)kf; - Position pos = pkf.getPosition(); - spline.addPoint(pos); - keyframes.add(pkf); - } + 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) { @@ -118,7 +116,7 @@ public class PathPreviewRenderer { } } - private class DistanceComparator implements Comparator { + private class DistanceComparator implements Comparator> { private double playerX, playerY, playerZ; @@ -129,8 +127,8 @@ public class PathPreviewRenderer { } @Override - public int compare(PositionKeyframe o1, PositionKeyframe o2) { - return -(new Double(o1.getPosition().distanceSquared(playerX, playerY, playerZ)).compareTo(o2.getPosition().distanceSquared(playerX, playerY, playerZ))); + public int compare(Keyframe o1, Keyframe o2) { + return -(new Double(o1.getValue().distanceSquared(playerX, playerY, playerZ)).compareTo(o2.getValue().distanceSquared(playerX, playerY, playerZ))); } @Override @@ -162,7 +160,7 @@ public class PathPreviewRenderer { renderer.setTranslation(0, 0, 0); } - private void drawPoint(double playerX, double playerY, double playerZ, PositionKeyframe kf) { + private void drawPoint(double playerX, double playerY, double playerZ, Keyframe kf) { GlStateManager.pushMatrix(); GlStateManager.enableTexture2D(); GlStateManager.enableLighting(); @@ -176,7 +174,7 @@ public class PathPreviewRenderer { mc.renderEngine.bindTexture(GuiReplayOverlay.replay_gui); - Position pos1 = kf.getPosition(); + Position pos1 = kf.getValue(); double x = pos1.getX() - playerX; double y = pos1.getY() - playerY; @@ -205,10 +203,10 @@ public class PathPreviewRenderer { posY += size; } - if(kf.getSpectatedEntityID() != null) { + if(pos1.getSpectatedEntityID() != null) { posX += size; } - + float minX = -0.5f; float minY = -0.5f; float maxX = 0.5f; diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java index c63ffd6c..700e8c33 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java @@ -3,10 +3,12 @@ package eu.crushedpixel.replaymod.replay; import com.mojang.authlib.GameProfile; import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.assets.AssetRepository; +import eu.crushedpixel.replaymod.assets.CustomImageObject; import eu.crushedpixel.replaymod.entities.CameraEntity; import eu.crushedpixel.replaymod.events.KeyframesModifyEvent; import eu.crushedpixel.replaymod.events.ReplayExitEvent; import eu.crushedpixel.replaymod.holders.*; +import eu.crushedpixel.replaymod.interpolation.KeyframeList; import eu.crushedpixel.replaymod.registry.PlayerHandler; import eu.crushedpixel.replaymod.settings.RenderOptions; import eu.crushedpixel.replaymod.utils.ReplayFile; @@ -41,15 +43,22 @@ public class ReplayHandler { private static Minecraft mc = Minecraft.getMinecraft(); private static OpenEmbeddedChannel channel; private static int realTimelinePosition = 0; + private static Keyframe selectedKeyframe; + private static MarkerKeyframe selectedMarkerKeyframe; + private static boolean inPath = false; private static CameraEntity cameraEntity; - private static List keyframes = new ArrayList(); + + private static KeyframeList> positionKeyframes = new KeyframeList>(); + private static KeyframeList> timeKeyframes = new KeyframeList>(); + private static boolean inReplay = false; private static Entity currentEntity = null; private static Position lastPosition = null; private static MarkerKeyframe[] initialMarkers = new MarkerKeyframe[0]; + private static List markerKeyframes = new ArrayList(); private static float cameraTilt = 0; @@ -85,18 +94,12 @@ public class ReplayHandler { } public static MarkerKeyframe[] getMarkers() { - List markers = new ArrayList(); - for(Keyframe kf : keyframes) { - if(kf instanceof MarkerKeyframe) markers.add((MarkerKeyframe)kf); - } - return markers.toArray(new MarkerKeyframe[markers.size()]); + return markerKeyframes.toArray(new MarkerKeyframe[markerKeyframes.size()]); } public static void setMarkers(MarkerKeyframe[] m, boolean write) { - for(Keyframe kf : new ArrayList(keyframes)) { - if(kf instanceof MarkerKeyframe) keyframes.remove(kf); - } - Collections.addAll(keyframes, m); + markerKeyframes.clear(); + Collections.addAll(markerKeyframes, m); if(write) { try { @@ -109,8 +112,6 @@ public class ReplayHandler { e.printStackTrace(); } } - - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(keyframes)); } public static void useKeyframePresetFromRepository(int index) { @@ -118,15 +119,17 @@ public class ReplayHandler { } public static void useKeyframePreset(Keyframe[] kfs) { - MarkerKeyframe[] markers = getMarkers(); + positionKeyframes.clear(); + timeKeyframes.clear(); + for(Keyframe kf : kfs) { + if(kf.getValue() instanceof Position) { + positionKeyframes.add(kf); + } else if(kf.getValue() instanceof TimestampValue) { + timeKeyframes.add(kf); + } + } - keyframes = new ArrayList(Arrays.asList(kfs)); - - Collections.addAll(keyframes, markers); - - if(!(selectedKeyframe instanceof MarkerKeyframe)) selectedKeyframe = null; - - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(keyframes)); + fireKeyframesModifyEvent(); } public static void spectateEntity(Entity e) { @@ -143,7 +146,7 @@ public class ReplayHandler { public static void spectateCamera() { if(currentEntity != null) { - Position prev = new Position(currentEntity); + Position prev = new Position(currentEntity, false); cameraEntity.movePath(prev); } currentEntity = cameraEntity; @@ -222,136 +225,75 @@ public class ReplayHandler { cameraTilt += tilt; } - public static void sortKeyframes() { - Collections.sort(keyframes, new KeyframeComparator()); + public static void toggleMarker() { + if(selectedMarkerKeyframe != null) markerKeyframes.remove(selectedMarkerKeyframe); + else { + Position pos = new Position(mc.getRenderViewEntity(), false); + int timestamp = ReplayMod.replaySender.currentTimeStamp(); + markerKeyframes.add(new MarkerKeyframe(timestamp, pos, null)); + } } - public static void toggleMarker() { - if(selectedKeyframe instanceof MarkerKeyframe) removeKeyframe(selectedKeyframe); - else { - Position pos = new Position(mc.getRenderViewEntity()); - int timestamp = ReplayMod.replaySender.currentTimeStamp(); - addKeyframe(new MarkerKeyframe(pos, timestamp, null)); + public static void addTimeKeyframe(Keyframe keyframe) { + timeKeyframes.add(keyframe); + selectKeyframe(keyframe); + + fireKeyframesModifyEvent(); + } + + public static void addPositionKeyframe(Keyframe keyframe) { + positionKeyframes.add(keyframe); + selectKeyframe(keyframe); + + Float a = null; + Float b; + + for(Keyframe kf : positionKeyframes) { + if(!(kf.getValue() instanceof Position)) continue; + Keyframe pkf = (Keyframe)kf; + Position pos = pkf.getValue(); + b = (float)pos.getYaw() % 360; + if(a != null) { + float diff = b-a; + if(Math.abs(diff) > 180) { + b = a - (360 - diff) % 360; + pos.setYaw(b); + pkf.setValue(pos); + } + } + a = b; } + + fireKeyframesModifyEvent(); } public static void addKeyframe(Keyframe keyframe) { - keyframes.add(keyframe); - selectKeyframe(keyframe); - - if(keyframe instanceof PositionKeyframe) { - Float a = null; - Float b; - - for(Keyframe kf : keyframes) { - if(!(kf instanceof PositionKeyframe)) continue; - PositionKeyframe pkf = (PositionKeyframe)kf; - Position pos = pkf.getPosition(); - b = pos.getYaw() % 360; - if(a != null) { - float diff = b-a; - if(Math.abs(diff) > 180) { - b = a - (360 - diff) % 360; - pos.setYaw(b); - pkf.setPosition(pos); - } - } - a = b; - } + if(keyframe.getValue() instanceof Position) { + addPositionKeyframe(keyframe); + } else if(keyframe.getValue() instanceof TimestampValue) { + addTimeKeyframe(keyframe); } - - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(keyframes)); } public static void removeKeyframe(Keyframe keyframe) { - keyframes.remove(keyframe); + if(keyframe.getValue() instanceof Position) { + positionKeyframes.remove(keyframe); + } else if(keyframe.getValue() instanceof TimestampValue) { + timeKeyframes.remove(keyframe); + } + if(keyframe == selectedKeyframe) { selectKeyframe(null); - } else { - sortKeyframes(); } - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(keyframes)); - } - - public static int getKeyframeIndex(TimeKeyframe timeKeyframe) { - int index = 0; - for(Keyframe kf : keyframes) { - if(kf == timeKeyframe) return index; - else if(kf instanceof TimeKeyframe) index++; - } - return -1; - } - - public static int getKeyframeIndex(PositionKeyframe posKeyframe) { - int index = 0; - for(Keyframe kf : keyframes) { - if(kf == posKeyframe) return index; - else if(kf instanceof PositionKeyframe) index++; - } - return -1; - } - - public static int getPosKeyframeCount() { - int size = 0; - for(Keyframe kf : keyframes) { - if(kf instanceof PositionKeyframe) size++; - } - return size; - } - - public static int getTimeKeyframeCount() { - int size = 0; - for(Keyframe kf : keyframes) { - if(kf instanceof TimeKeyframe) size++; - } - return size; - } - - public static TimeKeyframe getClosestTimeKeyframeForRealTime(int realTime, int tolerance) { - List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof TimeKeyframe)) continue; - if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { - found.add((TimeKeyframe) kf); - } - } - - TimeKeyframe closest = null; - - for(TimeKeyframe kf : found) { - if(closest == null || Math.abs(closest.getTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) { - closest = kf; - } - } - return closest; - } - - public static PositionKeyframe getClosestPlaceKeyframeForRealTime(int realTime, int tolerance) { - List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof PositionKeyframe)) continue; - if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { - found.add((PositionKeyframe) kf); - } - } - - PositionKeyframe closest = null; - - for(PositionKeyframe kf : found) { - if(closest == null || Math.abs(closest.getRealTimestamp() - realTime) > Math.abs(kf.getRealTimestamp() - realTime)) { - closest = kf; - } - } - return closest; + fireKeyframesModifyEvent(); } public static MarkerKeyframe getClosestMarkerForRealTime(int realTime, int tolerance) { List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof MarkerKeyframe)) continue; + for(MarkerKeyframe kf : markerKeyframes) { if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { - found.add((MarkerKeyframe) kf); + found.add(kf); } } @@ -365,76 +307,11 @@ public class ReplayHandler { return closest; } - public static PositionKeyframe getPreviousPositionKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; - PositionKeyframe backup = null; - List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof PositionKeyframe)) continue; - if(kf.getRealTimestamp() < realTime) { - found.add((PositionKeyframe)kf); - } else if(kf.getRealTimestamp() == realTime) { - backup = (PositionKeyframe)kf; - } - } - - if(found.size() > 0) - return found.get(found.size() - 1); //last element is nearest - else return backup; - } - - public static PositionKeyframe getNextPositionKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; - PositionKeyframe backup = null; - for(Keyframe kf : keyframes) { - if(!(kf instanceof PositionKeyframe)) continue; - if(kf.getRealTimestamp() > realTime) { - return (PositionKeyframe)kf; //first found element is next - } else if(kf.getRealTimestamp() == realTime) { - backup = (PositionKeyframe)kf; - } - } - return backup; - } - - public static TimeKeyframe getPreviousTimeKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; - TimeKeyframe backup = null; - List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof TimeKeyframe)) continue; - if(kf.getRealTimestamp() < realTime) { - found.add((TimeKeyframe)kf); - } else if(kf.getRealTimestamp() == realTime) { - backup = (TimeKeyframe)kf; - } - } - - if(found.size() > 0) - return found.get(found.size() - 1); //last element is nearest - else return backup; - } - - public static TimeKeyframe getNextTimeKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; - TimeKeyframe backup = null; - for(Keyframe kf : keyframes) { - if(!(kf instanceof TimeKeyframe)) continue; - if(kf.getRealTimestamp() > realTime) { - return (TimeKeyframe) kf; //first found element is next - } else if(kf.getRealTimestamp() == realTime) { - backup = (TimeKeyframe)kf; - } - } - return backup; - } - public static MarkerKeyframe getPreviousMarkerKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; + if(markerKeyframes.isEmpty()) return null; MarkerKeyframe backup = null; List found = new ArrayList(); - for(Keyframe kf : keyframes) { - if(!(kf instanceof MarkerKeyframe)) continue; + for(MarkerKeyframe kf : markerKeyframes) { if(kf.getRealTimestamp() < realTime) { found.add((MarkerKeyframe)kf); } else if(kf.getRealTimestamp() == realTime) { @@ -448,21 +325,32 @@ public class ReplayHandler { } public static MarkerKeyframe getNextMarkerKeyframe(int realTime) { - if(keyframes.isEmpty()) return null; + if(markerKeyframes.isEmpty()) return null; MarkerKeyframe backup = null; - for(Keyframe kf : keyframes) { - if(!(kf instanceof MarkerKeyframe)) continue; + for(MarkerKeyframe kf : markerKeyframes) { if(kf.getRealTimestamp() > realTime) { - return (MarkerKeyframe) kf; //first found element is next + return kf; //first found element is next } else if(kf.getRealTimestamp() == realTime) { - backup = (MarkerKeyframe)kf; + backup = kf; } } return backup; } - public static List getKeyframes() { - return new ArrayList(keyframes); + public static KeyframeList> getPositionKeyframes() { + return positionKeyframes; + } + + public static KeyframeList> getTimeKeyframes() { + return timeKeyframes; + } + + public static KeyframeList getAllKeyframes() { + KeyframeList keyframeList = new KeyframeList(); + keyframeList.addAll(positionKeyframes); + keyframeList.addAll(timeKeyframes); + + return keyframeList; } public static void resetKeyframes(final boolean resetMarkers, boolean callback) { @@ -483,19 +371,15 @@ public class ReplayHandler { } private static void resetKeyframes(boolean resetMarkers) { - MarkerKeyframe[] markers = getMarkers(); - keyframes = new ArrayList(); + timeKeyframes.clear(); + positionKeyframes.clear(); + selectKeyframe(null); - if(!resetMarkers) { - Collections.addAll(keyframes, markers); - - if(!(selectedKeyframe instanceof MarkerKeyframe)) - selectKeyframe(null); - } else { - selectKeyframe(null); + if(resetMarkers) { + markerKeyframes.clear(); } - FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(keyframes)); + fireKeyframesModifyEvent(); } public static boolean isSelected(Keyframe kf) { @@ -504,7 +388,6 @@ public class ReplayHandler { public static void selectKeyframe(Keyframe kf) { selectedKeyframe = kf; - sortKeyframes(); } public static boolean isInReplay() { @@ -681,44 +564,6 @@ public class ReplayHandler { return currentReplayFile == null ? null : currentReplayFile.getFile(); } - public static TimeKeyframe getFirstTimeKeyframe() { - Keyframe sel = getSelectedKeyframe(); - sortKeyframes(); - for(Keyframe k : getKeyframes()) { - if(k instanceof TimeKeyframe) { - selectKeyframe(sel); - return (TimeKeyframe)k; - } - } - selectKeyframe(sel); - return null; - } - - public static PositionKeyframe getFirstPositionKeyframe() { - Keyframe sel = getSelectedKeyframe(); - sortKeyframes(); - for(Keyframe k : getKeyframes()) { - if(k instanceof PositionKeyframe) { - selectKeyframe(sel); - return (PositionKeyframe)k; - } - } - selectKeyframe(sel); - return null; - } - - public static TimeKeyframe getLastTimeKeyframe() { - ArrayList rev = new ArrayList(getKeyframes()); - Collections.reverse(rev); - - for(Keyframe k : rev) { - if(k instanceof TimeKeyframe) { - return (TimeKeyframe)k; - } - } - return null; - } - public static void syncTimeCursor(boolean shiftMode) { selectKeyframe(null); @@ -726,21 +571,21 @@ public class ReplayHandler { int prevTime, prevRealTime; - TimeKeyframe keyframe; + Keyframe keyframe; //if shift is down, it will refer to the previous Time Keyframe instead of the last one if(shiftMode) { int realTime = getRealTimelineCursor(); - keyframe = getPreviousTimeKeyframe(realTime); + keyframe = timeKeyframes.getPreviousKeyframe(realTime); } else { - keyframe = getLastTimeKeyframe(); + keyframe = timeKeyframes.last(); } if(keyframe == null) { prevTime = 0; prevRealTime = 0; } else { - prevTime = keyframe.getTimestamp(); + prevTime = (int)keyframe.getValue().value; prevRealTime = keyframe.getRealTimestamp(); } @@ -760,4 +605,9 @@ public class ReplayHandler { public static void setCustomImageObjects(List objects) { customImageObjects = objects; } + + public static void fireKeyframesModifyEvent() { + FMLCommonHandler.instance().bus().post(new KeyframesModifyEvent(positionKeyframes, timeKeyframes)); + + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java index b0a2dd57..cddd0614 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java @@ -4,11 +4,9 @@ import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.chat.ChatMessageHandler.ChatMessageType; import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.Position; -import eu.crushedpixel.replaymod.holders.PositionKeyframe; -import eu.crushedpixel.replaymod.holders.TimeKeyframe; -import eu.crushedpixel.replaymod.interpolation.LinearPoint; -import eu.crushedpixel.replaymod.interpolation.LinearTimestamp; -import eu.crushedpixel.replaymod.interpolation.SplinePoint; +import eu.crushedpixel.replaymod.holders.TimestampValue; +import eu.crushedpixel.replaymod.interpolation.GenericLinearInterpolation; +import eu.crushedpixel.replaymod.interpolation.GenericSplineInterpolation; import eu.crushedpixel.replaymod.settings.RenderOptions; import eu.crushedpixel.replaymod.timer.EnchantmentTimer; import eu.crushedpixel.replaymod.timer.ReplayTimer; @@ -28,9 +26,9 @@ public class ReplayProcess { private static boolean linear = false; - private static SplinePoint motionSpline = null; - private static LinearPoint motionLinear = null; - private static LinearTimestamp timeLinear = null; + private static GenericSplineInterpolation motionSpline = null; + private static GenericLinearInterpolation motionLinear = null; + private static GenericLinearInterpolation timeLinear = null; private static double previousReplaySpeed = 0; @@ -77,20 +75,19 @@ public class ReplayProcess { ReplayMod.chatMessageHandler.initialize(); //if not enough keyframes, abort and leave chat message - if(ReplayHandler.getPosKeyframeCount() < 2 || ReplayHandler.getTimeKeyframeCount() < 1) { + if(ReplayHandler.getPositionKeyframes().size() < 2 || ReplayHandler.getTimeKeyframes().size() < 1) { ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.morekeyframes", ChatMessageType.WARNING); return; } - ReplayHandler.sortKeyframes(); ReplayHandler.setInPath(true); ReplayMod.replaySender.setAsyncMode(false); if (renderOptions == null) { - //gets first Timestamp and sets Replay Time to it - TimeKeyframe tf = ReplayHandler.getFirstTimeKeyframe(); + //gets first Value and sets Replay Time to it + Keyframe tf = ReplayHandler.getTimeKeyframes().first(); if (tf != null) { - int ts = tf.getTimestamp(); + int ts = (int)tf.getValue().value; if (ts < ReplayMod.replaySender.currentTimeStamp()) { mc.displayGuiScreen(null); } @@ -165,37 +162,29 @@ public class ReplayProcess { if(justCheck) return; - int posCount = ReplayHandler.getPosKeyframeCount(); - int timeCount = ReplayHandler.getTimeKeyframeCount(); + int posCount = ReplayHandler.getPositionKeyframes().size(); + int timeCount = ReplayHandler.getTimeKeyframes().size(); if(!linear && motionSpline == null) { //set up spline path - motionSpline = new SplinePoint(); - for(Keyframe kf : ReplayHandler.getKeyframes()) { - if(kf instanceof PositionKeyframe) { - PositionKeyframe pkf = (PositionKeyframe) kf; - Position pos = pkf.getPosition(); - motionSpline.addPoint(pos); - } + motionSpline = new GenericSplineInterpolation(); + for(Keyframe kf : ReplayHandler.getPositionKeyframes()) { + motionSpline.addPoint(kf.getValue()); } } if(linear && motionLinear == null) { //set up linear path - motionLinear = new LinearPoint(); - for(Keyframe kf : ReplayHandler.getKeyframes()) { - if(kf instanceof PositionKeyframe) { - PositionKeyframe pkf = (PositionKeyframe) kf; - Position pos = pkf.getPosition(); - motionLinear.addPoint(pos); - } + motionLinear = new GenericLinearInterpolation(); + for(Keyframe kf : ReplayHandler.getPositionKeyframes()) { + motionLinear.addPoint(kf.getValue()); } } if(timeLinear == null) { - timeLinear = new LinearTimestamp(); - for(Keyframe kf : ReplayHandler.getKeyframes()) { - if(kf instanceof TimeKeyframe) { - timeLinear.addPoint(((TimeKeyframe) kf).getTimestamp()); + timeLinear = new GenericLinearInterpolation(); + for(Keyframe kf : ReplayHandler.getTimeKeyframes()) { + if(kf.getValue() instanceof TimestampValue) { + timeLinear.addPoint(kf.getValue()); } } } @@ -211,16 +200,15 @@ public class ReplayProcess { int curRealReplayTime = (int) (lastRealReplayTime + timeStep); - PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(curRealReplayTime); - PositionKeyframe nextPos = ReplayHandler.getNextPositionKeyframe(curRealReplayTime); - + Keyframe lastPos = ReplayHandler.getPositionKeyframes().getPreviousKeyframe(curRealReplayTime); + Keyframe nextPos = ReplayHandler.getPositionKeyframes().getNextKeyframe(curRealReplayTime); boolean spectating = false; //if it's between two spectator keyframes sharing the same entity, spectate this entity if(lastPos != null && nextPos != null) { - if(lastPos.getSpectatedEntityID() != null && nextPos.getSpectatedEntityID() != null) { - if(lastPos.getSpectatedEntityID().equals(nextPos.getSpectatedEntityID())) { + if(lastPos.getValue().getSpectatedEntityID() != null && nextPos.getValue().getSpectatedEntityID() != null) { + if(lastPos.getValue().getSpectatedEntityID().equals(nextPos.getValue().getSpectatedEntityID())) { spectating = true; } } @@ -245,8 +233,8 @@ public class ReplayProcess { } } - TimeKeyframe lastTime = ReplayHandler.getPreviousTimeKeyframe(curRealReplayTime); - TimeKeyframe nextTime = ReplayHandler.getNextTimeKeyframe(curRealReplayTime); + Keyframe lastTime = ReplayHandler.getTimeKeyframes().getPreviousKeyframe(curRealReplayTime); + Keyframe nextTime = ReplayHandler.getTimeKeyframes().getNextKeyframe(curRealReplayTime); int lastTimeStamp = 0; int nextTimeStamp = 0; @@ -271,7 +259,7 @@ public class ReplayProcess { } if(!(nextTime == null || lastTime == null)) { - curSpeed = ((double) ((nextTime.getTimestamp() - lastTime.getTimestamp()))) / ((double) ((nextTimeStamp - lastTimeStamp))); + curSpeed = ((double) (((int)nextTime.getValue().value - (int)lastTime.getValue().value))) / ((double) ((nextTimeStamp - lastTimeStamp))); } if(lastTimeStamp == nextTimeStamp) { @@ -292,47 +280,46 @@ public class ReplayProcess { float currentTimeStepPerc = (float) currentTime / (float) currentTimeDiff; //The percentage of the travelled path between the current timestamps if(Float.isInfinite(currentTimeStepPerc)) currentTimeStepPerc = 0; - float splinePos = ((float) ReplayHandler.getKeyframeIndex(lastPos) + currentPosStepPerc) / (float) (posCount - 1); - float timePos = ((float) ReplayHandler.getKeyframeIndex(lastTime) + currentTimeStepPerc) / (float) (timeCount - 1); + float splinePos = ((float) ReplayHandler.getPositionKeyframes().indexOf(lastPos) + currentPosStepPerc) / (float) (posCount - 1); + float timePos = ((float) ReplayHandler.getTimeKeyframes().indexOf(lastTime) + currentTimeStepPerc) / (float) (timeCount - 1); if(!spectating) { ReplayHandler.spectateCamera(); - Position pos = null; + Position pos = new Position(); if(posCount > 1) { if(!linear) { - pos = motionSpline.getPoint(Math.max(0, Math.min(1, splinePos))); + motionSpline.applyPoint(Math.max(0, Math.min(1, splinePos)), pos); } else { - pos = motionLinear.getPoint(Math.max(0, Math.min(1, splinePos))); + motionLinear.applyPoint(Math.max(0, Math.min(1, splinePos)), pos); } } else { if(posCount == 1) { - PositionKeyframe keyframe = ReplayHandler.getFirstPositionKeyframe(); + Keyframe keyframe = ReplayHandler.getPositionKeyframes().first(); assert keyframe != null; - pos = keyframe.getPosition(); + pos = keyframe.getValue(); } } if(pos != null) { - ReplayHandler.setCameraTilt(pos.getRoll()); + ReplayHandler.setCameraTilt((float)pos.getRoll()); ReplayHandler.getCameraEntity().movePath(pos); } } else { - ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getSpectatedEntityID())); + ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getValue().getSpectatedEntityID())); } Integer curTimestamp = null; if(timeLinear != null && timeCount > 1) { - curTimestamp = timeLinear.getPoint(Math.max(0, Math.min(1, timePos))); + TimestampValue timestampValue = new TimestampValue(); + timeLinear.applyPoint(Math.max(0, Math.min(1, timePos)), timestampValue); + curTimestamp = (int) timestampValue.value; } if(!isVideoRecording()) ReplayMod.replaySender.setReplaySpeed(curSpeed); - //if(curSpeed > 0) if(curTimestamp != null) ReplayMod.replaySender.sendPacketsTill(curTimestamp); - //splinePos = (index of last entry + add) / total entries - lastRealReplayTime = curRealReplayTime; lastRealTime = curTime; diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java index 26fedc28..5ed03e91 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java @@ -69,7 +69,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { protected boolean asyncMode; /** - * Timestamp of the last packet sent in milliseconds since the start. + * Value of the last packet sent in milliseconds since the start. */ protected int lastTimeStamp; @@ -91,7 +91,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { /** * The next packet that should be sent. - * This is required as some actions such as jumping to a specified timestamp have to peek at the next packet. + * This is required as some actions such as jumping to a specified value have to peek at the next packet. */ protected PacketData nextPacket; @@ -188,8 +188,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } /** - * Return the timestamp of the last packet sent. - * @return The timestamp in milliseconds since the start of the replay + * Return the value of the last packet sent. + * @return The value in milliseconds since the start of the replay */ public int currentTimeStamp() { return lastTimeStamp; @@ -450,7 +450,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { private long lastPacketSent; /** - * There is no waiting performed until a packet with at least this timestamp is reached (but not yet sent). + * There is no waiting performed until a packet with at least this value is reached (but not yet sent). * If this is -1, then timing is normal. */ private long desiredTimeStamp = -1; @@ -573,7 +573,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { /** * Return whether this replay sender is currently rushing. When rushing, all packets are sent without waiting until - * a specified timestamp is passed. + * a specified value is passed. * @return {@code true} if currently rushing, {@code false} otherwise */ public boolean isHurrying() { @@ -588,19 +588,19 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } /** - * Return the timestamp to which this replay sender is currently rushing. All packets with an lower or equal - * timestamp will be sent out without any sleeping. - * @return The timestamp in milliseconds since the start of the replay + * Return the value to which this replay sender is currently rushing. All packets with an lower or equal + * value will be sent out without any sleeping. + * @return The value in milliseconds since the start of the replay */ public long getDesiredTimestamp() { return desiredTimeStamp; } /** - * Jumps to the specified timestamp when in async mode by rushing all packets until one with a timestamp greater - * than the specified timestamp is found. - * If the timestamp has already passed, this causes the replay to restart and then rush all packets. - * @param millis Timestamp in milliseconds since the start of the replay + * Jumps to the specified value when in async mode by rushing all packets until one with a value greater + * than the specified value is found. + * If the value has already passed, this causes the replay to restart and then rush all packets. + * @param millis Value in milliseconds since the start of the replay */ public void jumpToTime(int millis) { Preconditions.checkState(asyncMode, "Can only jump in async mode. Use sendPacketsTill(int) instead."); @@ -632,9 +632,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { ///////////////////////////////////////////////////////// /** - * Sends all packets until the specified timestamp is reached (inclusive). - * If the timestamp is smaller than the last packet sent, the replay is restarted from the beginning. - * @param timestamp The timestamp in milliseconds since the beginning of this replay + * Sends all packets until the specified value is reached (inclusive). + * If the value is smaller than the last packet sent, the replay is restarted from the beginning. + * @param timestamp The value in milliseconds since the beginning of this replay */ public void sendPacketsTill(int timestamp) { Preconditions.checkState(!asyncMode, "This method cannot be used in async mode. Use jumpToTime(int) instead."); @@ -682,7 +682,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { // Process packet channelRead(ctx, pd.getByteArray()); - // Store last timestamp + // Store last value lastTimeStamp = nextTimeStamp; } catch (EOFException eof) { // Shit! We hit the end before finishing our job! What shall we do now? diff --git a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java index de5119ae..39b30345 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java @@ -5,12 +5,11 @@ import eu.crushedpixel.replaymod.entities.CameraEntity; import eu.crushedpixel.replaymod.gui.GuiVideoRenderer; import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.Position; -import eu.crushedpixel.replaymod.holders.PositionKeyframe; -import eu.crushedpixel.replaymod.holders.TimeKeyframe; +import eu.crushedpixel.replaymod.holders.TimestampValue; +import eu.crushedpixel.replaymod.interpolation.GenericLinearInterpolation; +import eu.crushedpixel.replaymod.interpolation.GenericSplineInterpolation; import eu.crushedpixel.replaymod.interpolation.Interpolation; -import eu.crushedpixel.replaymod.interpolation.LinearPoint; -import eu.crushedpixel.replaymod.interpolation.LinearTimestamp; -import eu.crushedpixel.replaymod.interpolation.SplinePoint; +import eu.crushedpixel.replaymod.interpolation.KeyframeList; import eu.crushedpixel.replaymod.renderer.ChunkLoadingRenderGlobal; import eu.crushedpixel.replaymod.replay.ReplayHandler; import eu.crushedpixel.replaymod.replay.ReplaySender; @@ -44,7 +43,7 @@ public class VideoRenderer { private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal; private Interpolation movement; - private Interpolation time; + private Interpolation time; private int framesDone; private int totalFrames; @@ -80,9 +79,13 @@ public class VideoRenderer { // Note that it is impossible to also get the interpolation between their latest position // and the one in the recording correct as there's no reliable way to tell when the server ticks // or when we should be done with the interpolation of the entity - int videoStart = time.getPoint(0); + TimestampValue timestampValue = new TimestampValue(); + time.applyPoint(0, timestampValue); + + int videoStart = (int) timestampValue.value; + if (videoStart > 1000) { - int replayTime = time.getPoint(0) - 1000; + int replayTime = videoStart - 1000; timer.elapsedPartialTicks = timer.renderPartialTicks = 0; timer.timerSpeed = 1; while (replayTime < videoStart) { @@ -132,26 +135,33 @@ public class VideoRenderer { fps = options.getFps(); if (options.isLinearMovement()) { - movement = new LinearPoint(); + movement = new GenericLinearInterpolation(); } else { - movement = new SplinePoint(); + movement = new GenericSplineInterpolation(); } - time = new LinearTimestamp(); + time = new GenericLinearInterpolation(); int duration = 0; int posKeyframes = 0; - for (Keyframe keyframe : ReplayHandler.getKeyframes()) { + + for(Keyframe keyframe : ReplayHandler.getPositionKeyframes()) { if (keyframe.getRealTimestamp() > duration) { duration = keyframe.getRealTimestamp(); } - if(keyframe instanceof PositionKeyframe) { - movement.addPoint(((PositionKeyframe) keyframe).getPosition()); - posKeyframes++; - } - if (keyframe instanceof TimeKeyframe) { - time.addPoint(((TimeKeyframe) keyframe).getTimestamp()); - } + + movement.addPoint(keyframe.getValue()); + posKeyframes++; } + + for(Keyframe keyframe : ReplayHandler.getTimeKeyframes()) { + if (keyframe.getRealTimestamp() > duration) { + duration = keyframe.getRealTimestamp(); + } + + int timestamp = (int)keyframe.getValue().value; + time.addPoint(new TimestampValue(timestamp)); + } + totalFrames = duration*fps/1000; if (posKeyframes >= 2) { @@ -195,6 +205,8 @@ public class VideoRenderer { } private void updateCam() { + KeyframeList> positionKeyframes = ReplayHandler.getPositionKeyframes(); + if (ReplayHandler.getCameraEntity() == null) { if (mc.theWorld == null) { return; // World hasn't been sent yet @@ -202,19 +214,19 @@ public class VideoRenderer { ReplayHandler.setCameraEntity(new CameraEntity(mc.theWorld)); } int videoTime = framesDone * 1000 / fps; - int posCount = ReplayHandler.getPosKeyframeCount(); + int posCount = ReplayHandler.getPositionKeyframes().size(); - Position pos; - PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(videoTime); - PositionKeyframe nextPos = null; + Position pos = new Position(); + Keyframe lastPos = positionKeyframes.getPreviousKeyframe(videoTime); + Keyframe nextPos = null; if (movement == null || lastPos == null) { // Stay at one position, no movement - PositionKeyframe keyframe = ReplayHandler.getNextPositionKeyframe(-1); + Keyframe keyframe = positionKeyframes.getNextKeyframe(-1); assert keyframe != null; - pos = keyframe.getPosition(); + pos = keyframe.getValue(); } else { // Position interpolation - nextPos = ReplayHandler.getNextPositionKeyframe(videoTime); + nextPos = positionKeyframes.getNextKeyframe(videoTime); int lastPosStamp = lastPos.getRealTimestamp(); int nextPosStamp = (nextPos == null ? lastPos : nextPos).getRealTimestamp(); @@ -223,16 +235,16 @@ public class VideoRenderer { float diffPct = (float) (videoTime - lastPosStamp) / diffLength; if(Float.isInfinite(diffPct) || Float.isNaN(diffPct)) diffPct = 0; - float totalPct = (ReplayHandler.getKeyframeIndex(lastPos) + diffPct) / (posCount-1); - pos = movement.getPoint(Math.max(0, Math.min(1, totalPct))); + float totalPct = (positionKeyframes.indexOf(lastPos) + diffPct) / (posCount-1); + movement.applyPoint(Math.max(0, Math.min(1, totalPct)), pos); } boolean spectating = false; //if it's between two spectator keyframes sharing the same entity, spectate this entity if(lastPos != null && nextPos != null) { - if(lastPos.getSpectatedEntityID() != null && nextPos.getSpectatedEntityID() != null) { - if(lastPos.getSpectatedEntityID().equals(nextPos.getSpectatedEntityID())) { + if(lastPos.getValue().getSpectatedEntityID() != null && nextPos.getValue().getSpectatedEntityID() != null) { + if(lastPos.getValue().getSpectatedEntityID().equals(nextPos.getValue().getSpectatedEntityID())) { spectating = true; } } @@ -245,24 +257,26 @@ public class VideoRenderer { ReplayHandler.spectateEntity(ReplayHandler.getCameraEntity()); if(pos != null) { - ReplayHandler.setCameraTilt(pos.getRoll()); + ReplayHandler.setCameraTilt((float)pos.getRoll()); ReplayHandler.getCameraEntity().movePath(pos); mc.entityRenderer.fovModifierHand = mc.entityRenderer.fovModifierHandPrev = 1; } } else { - ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getSpectatedEntityID())); + ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getValue().getSpectatedEntityID())); } } private void updateTime(Timer timer, int framesDone) { + KeyframeList> timeKeyframes = ReplayHandler.getTimeKeyframes(); + int videoTime = framesDone * 1000 / fps; - int timeCount = ReplayHandler.getTimeKeyframeCount(); + int timeCount = timeKeyframes.size(); // WARNING: The rest of this method contains some magic for which Marius is responsible // Time interpolation - TimeKeyframe lastTime = ReplayHandler.getPreviousTimeKeyframe(videoTime); - TimeKeyframe nextTime = ReplayHandler.getNextTimeKeyframe(videoTime); + Keyframe lastTime = timeKeyframes.getPreviousKeyframe(videoTime); + Keyframe nextTime = timeKeyframes.getNextKeyframe(videoTime); int lastTimeStamp = 0; int nextTimeStamp = 0; @@ -286,7 +300,7 @@ public class VideoRenderer { } if(!(nextTime == null || lastTime == null)) { - curSpeed = ((double)((nextTime.getTimestamp()-lastTime.getTimestamp())))/((double)((nextTimeStamp-lastTimeStamp))); + curSpeed = ((double)(((int)nextTime.getValue().value-(int)lastTime.getValue().value)))/((double)((nextTimeStamp-lastTimeStamp))); } if(lastTimeStamp == nextTimeStamp) { @@ -301,9 +315,11 @@ public class VideoRenderer { float currentTimeStepPerc = (float)currentTime/(float)currentTimeDiff; //The percentage of the travelled path between the current timestamps if(Float.isInfinite(currentTimeStepPerc) || Float.isNaN(currentTimeStepPerc)) currentTimeStepPerc = 0; - float timePos = (ReplayHandler.getKeyframeIndex(lastTime) + currentTimeStepPerc) / (timeCount-1f); + float timePos = (timeKeyframes.indexOf(lastTime) + currentTimeStepPerc) / (timeCount-1f); - Integer replayTime = time.getPoint(Math.max(0, Math.min(1, timePos))); + TimestampValue timestampValue = new TimestampValue(); + time.applyPoint(Math.max(0, Math.min(1, timePos)), timestampValue); + Integer replayTime = (int) timestampValue.value; if(replayTime != null) { replaySender.sendPacketsTill(replayTime); }