diff --git a/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java index 45ef7c72..5792fc7a 100644 --- a/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java +++ b/src/main/java/eu/crushedpixel/replaymod/assets/CustomImageObject.java @@ -32,7 +32,7 @@ public class CustomImageObject implements GuiEntryListEntry { @Getter private float textureWidth, textureHeight; - @Getter private Transformations transformations; + @Getter private Transformations transformations = new Transformations(); public void setLinkedAsset(UUID assetUUID) throws IOException { ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(assetUUID); diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiObjectManager.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiObjectManager.java index 31e0851d..d87b7b5f 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiObjectManager.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiObjectManager.java @@ -6,10 +6,12 @@ import eu.crushedpixel.replaymod.gui.elements.*; import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener; import eu.crushedpixel.replaymod.gui.elements.timelines.GuiTimeline; import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay; -import eu.crushedpixel.replaymod.holders.GuiEntryListValueEntry; +import eu.crushedpixel.replaymod.holders.*; import eu.crushedpixel.replaymod.interpolation.KeyframeList; import eu.crushedpixel.replaymod.replay.ReplayHandler; import eu.crushedpixel.replaymod.utils.MouseUtils; +import lombok.Getter; +import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.GlStateManager; @@ -18,14 +20,13 @@ import org.lwjgl.util.Point; import java.awt.*; import java.io.IOException; +import java.util.List; import java.util.UUID; public class GuiObjectManager extends GuiScreen { private boolean initialized = false; - private KeyframeList[] keyframeLists; - private GuiEntryList objectList; private GuiAdvancedButton addButton, removeButton; @@ -61,7 +62,7 @@ public class GuiObjectManager extends GuiScreen { GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() { @Override public void run() { - addKeyframe(line); + objectKeyframeTimeline.addKeyframe(line); } }, null); @@ -71,16 +72,14 @@ public class GuiObjectManager extends GuiScreen { GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() { @Override public void run() { - removeKeyframe(line); + objectKeyframeTimeline.removeKeyframe(line); } }, null); @Override public GuiElement delegate() { - boolean sel = false; //TODO - - if(sel) { + if(objectKeyframeTimeline.getSelectedKeyframeRow() == line) { return selected; } else { return normal; @@ -95,14 +94,6 @@ public class GuiObjectManager extends GuiScreen { }; } - private void addKeyframe(int line) { - - } - - private void removeKeyframe(int line) { - - } - @Override public void initGui() { if(!initialized) { @@ -161,8 +152,6 @@ public class GuiObjectManager extends GuiScreen { nameInput = new GuiAdvancedTextField(fontRendererObj, 0, 0, 0, 20); nameInput.hint = I18n.format("replaymod.gui.objects.properties.name"); - keyframeLists = new KeyframeList[5]; - objectList.addSelectionListener(new SelectionListener() { @Override public void onSelectionChanged(int selectionIndex) { @@ -187,6 +176,7 @@ public class GuiObjectManager extends GuiScreen { assetDropdown.setSelectionIndexQuietly(sel); + objectKeyframeTimeline.setTransformations(selectedObject.getTransformations()); } else { disableElements.setEnabled(false); } @@ -264,7 +254,7 @@ public class GuiObjectManager extends GuiScreen { int timelineWidth = this.width-10-timelineX + 2; int timelineHeight = this.height-10-10-timelineY + 2; - objectKeyframeTimeline = new GuiObjectKeyframeTimeline(timelineX, timelineY, timelineWidth, timelineHeight, keyframeLists); + objectKeyframeTimeline = new GuiObjectKeyframeTimeline(timelineX, timelineY, timelineWidth, timelineHeight); disableElements.addPart(objectKeyframeTimeline); timelineScrollbar = new GuiScrollbar(timelineX, this.height-15, timelineWidth-21) { @@ -389,28 +379,107 @@ public class GuiObjectManager extends GuiScreen { public class GuiObjectKeyframeTimeline extends GuiTimeline { - private KeyframeList[] keyframeLists; + private static final int KEYFRAME_X = 74; + private static final int KEYFRAME_Y = 35; + private static final int KEYFRAME_SIZE = 5; - public int x, y, width, height; + @Getter @Setter + private Transformations transformations; + + @Getter private int selectedKeyframeRow = -1; + @Getter private Keyframe selectedKeyframe = null; private boolean dragging = false; - public GuiObjectKeyframeTimeline(int x, int y, int width, int height, KeyframeList... keyframeLists) { + public GuiObjectKeyframeTimeline(int x, int y, int width, int height) { super(x, y, width, height); - this.keyframeLists = keyframeLists; - this.zoom = 0.1; this.timelineLength = 10 * 60 * 1000; this.showMarkers = true; } + @Override + public void draw(Minecraft mc, int mouseX, int mouseY) { + super.draw(mc, mouseX, mouseY); + + int bodyWidth = width - BORDER_LEFT - BORDER_RIGHT; + + long leftTime = Math.round(timeStart * timelineLength); + long rightTime = Math.round((timeStart + zoom) * timelineLength); + + double segmentLength = timelineLength * zoom; + + if(transformations != null) { + for(int i = 0; i < 5; i++) { + KeyframeList keyframes = transformations.getKeyframeListByID(i); + + //Draw Keyframe logos + for(Keyframe kf : (List) keyframes) { + drawKeyframe(kf, (int)(i * (height / 5f)) + 10, bodyWidth, leftTime, rightTime, segmentLength); + } + } + } + } + + private int getKeyframeX(int timestamp, long leftTime, int bodyWidth, double segmentLength) { + long positionInSegment = timestamp - leftTime; + double fractionOfSegment = positionInSegment / segmentLength; + return (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth); + } + + private void drawKeyframe(Keyframe kf, int y, int bodyWidth, long leftTime, long rightTime, double segmentLength) { + if (kf.getRealTimestamp() <= rightTime && kf.getRealTimestamp() >= leftTime) { + int textureX = KEYFRAME_X; + int textureY = KEYFRAME_Y; + y = positionY+y; + + int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, bodyWidth, segmentLength); + + if (kf == selectedKeyframe) { + textureX += KEYFRAME_SIZE; + } + + rect(keyframeX - 2, y, textureX, textureY, 5, 5); + } + } + @Override public boolean mouseClick(Minecraft mc, int mouseX, int mouseY, int button) { if(!enabled) return false; super.mouseClick(mc, mouseX, mouseY, button); int time = (int) getTimeAt(mouseX, mouseY); if(time != -1) { + boolean clicked = false; + + int tolerance = (int) (2 * Math.round(zoom * timelineLength / width)); + + if(transformations != null) { + for(int i = 0; i < 5; i++) { + int upper = positionY + (int)(i * (height / 5f)) + 10; + int lower = upper + KEYFRAME_SIZE; + if(mouseY >= upper && mouseY <= lower) { + KeyframeList keyframes = transformations.getKeyframeListByID(i); + Keyframe closest = keyframes.getClosestKeyframeForTimestamp(time, tolerance); + + selectedKeyframe = closest; + + if(selectedKeyframe != null) { + selectedKeyframeRow = i; + } else { + selectedKeyframeRow = -1; + } + + clicked = true; + } + } + } + + if(!clicked) { + selectedKeyframe = null; + selectedKeyframeRow = -1; + } + cursorPosition = time; dragging = true; return true; @@ -435,5 +504,65 @@ public class GuiObjectManager extends GuiScreen { super.mouseRelease(mc, mouseX, mouseY, button); this.dragging = false; } + + public void addKeyframe(int line) { + CustomImageObject currentObject = objectList.getElement(objectList.getSelectionIndex()); + if(currentObject != null) { + KeyframeList list = currentObject.getTransformations().getKeyframeListByID(line); + int timestamp = objectKeyframeTimeline.cursorPosition; + + Keyframe kf = null; + + switch(line) { + case 0: + + kf = new Keyframe(timestamp, new Position( + anchorXInput.getPreciseValue(), + anchorYInput.getPreciseValue(), + anchorZInput.getPreciseValue())); + break; + case 1: + kf = new Keyframe(timestamp, new Position( + positionXInput.getPreciseValue(), + positionYInput.getPreciseValue(), + positionZInput.getPreciseValue())); + break; + case 2: + kf = new Keyframe(timestamp, new Position( + orientationXInput.getPreciseValue(), + orientationYInput.getPreciseValue(), + orientationZInput.getPreciseValue())); + break; + case 3: + kf = new Keyframe(timestamp, new Position( + scaleXInput.getPreciseValue(), + scaleYInput.getPreciseValue(), + scaleZInput.getPreciseValue())); + break; + case 4: + kf = new Keyframe(timestamp, new NumberValue( + opacityInput.getPreciseValue() + )); + break; + } + + list.add(kf); + + selectedKeyframe = kf; + selectedKeyframeRow = line; + } + } + + public void removeKeyframe(int line) { + if(selectedKeyframeRow == line) { + CustomImageObject currentObject = objectList.getElement(objectList.getSelectionIndex()); + if(currentObject != null) { + KeyframeList list = currentObject.getTransformations().getKeyframeListByID(line); + list.remove(selectedKeyframe); + selectedKeyframe = null; + selectedKeyframeRow = -1; + } + } + } } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Transformation.java b/src/main/java/eu/crushedpixel/replaymod/holders/Transformation.java index b7e052d3..af766dec 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Transformation.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Transformation.java @@ -13,6 +13,4 @@ public class Transformation { private Position scale; private double opacity; - private float width, height; - } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java b/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java index d7227d7e..55805bf1 100644 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Transformations.java @@ -3,17 +3,43 @@ package eu.crushedpixel.replaymod.holders; import eu.crushedpixel.replaymod.interpolation.KeyframeList; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data @AllArgsConstructor +@NoArgsConstructor public class Transformations { - private KeyframeList anchorKeyframes, positionKeyframes, - orientationKeyframes, scaleKeyframes; - private KeyframeList opacityKeyframes; + private KeyframeList anchorKeyframes = new KeyframeList(); + private KeyframeList positionKeyframes = new KeyframeList(); + private KeyframeList orientationKeyframes = new KeyframeList(); + private KeyframeList scaleKeyframes = new KeyframeList(); + private KeyframeList opacityKeyframes = new KeyframeList(); + + public KeyframeList getKeyframeListByID(int id) { + switch(id) { + case 0: + return anchorKeyframes; + case 1: + return positionKeyframes; + case 2: + return orientationKeyframes; + case 3: + return scaleKeyframes; + case 4: + return opacityKeyframes; + default: + return null; + } + } public Transformation getTransformationForTimestamp(int timestamp) { - return null; + return new Transformation( + anchorKeyframes.getInterpolatedValueForTimestamp(timestamp, true), + positionKeyframes.getInterpolatedValueForTimestamp(timestamp, true), + orientationKeyframes.getInterpolatedValueForTimestamp(timestamp, true), + scaleKeyframes.getInterpolatedValueForTimestamp(timestamp, true), + opacityKeyframes.getInterpolatedValueForTimestamp(timestamp, true).value); } } diff --git a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java index 5871bad1..ce25a975 100644 --- a/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java +++ b/src/main/java/eu/crushedpixel/replaymod/interpolation/KeyframeList.java @@ -1,6 +1,5 @@ package eu.crushedpixel.replaymod.interpolation; -import eu.crushedpixel.replaymod.holders.AdvancedPosition; import eu.crushedpixel.replaymod.holders.Keyframe; import eu.crushedpixel.replaymod.holders.KeyframeComparator; @@ -108,7 +107,6 @@ public class KeyframeList extends ArrayList public Keyframe getClosestKeyframeForTimestamp(int realTime, int tolerance) { List> found = new ArrayList>(); for(Keyframe kf : this) { - if(!(kf.getValue() instanceof AdvancedPosition)) continue; if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) { found.add(kf); }