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); }