GuiObjectManager now allows users to set Keyframes for Transformations
Fixed a bug in KeyframeList#getClosestKeyframeForTimestamp that made it only work with AdvancedPosition Keyframes
This commit is contained in:
@@ -32,7 +32,7 @@ public class CustomImageObject implements GuiEntryListEntry {
|
|||||||
|
|
||||||
@Getter private float textureWidth, textureHeight;
|
@Getter private float textureWidth, textureHeight;
|
||||||
|
|
||||||
@Getter private Transformations transformations;
|
@Getter private Transformations transformations = new Transformations();
|
||||||
|
|
||||||
public void setLinkedAsset(UUID assetUUID) throws IOException {
|
public void setLinkedAsset(UUID assetUUID) throws IOException {
|
||||||
ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(assetUUID);
|
ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(assetUUID);
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import eu.crushedpixel.replaymod.gui.elements.*;
|
|||||||
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
||||||
import eu.crushedpixel.replaymod.gui.elements.timelines.GuiTimeline;
|
import eu.crushedpixel.replaymod.gui.elements.timelines.GuiTimeline;
|
||||||
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
|
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.interpolation.KeyframeList;
|
||||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||||
import eu.crushedpixel.replaymod.utils.MouseUtils;
|
import eu.crushedpixel.replaymod.utils.MouseUtils;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiScreen;
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
import net.minecraft.client.renderer.GlStateManager;
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
@@ -18,14 +20,13 @@ import org.lwjgl.util.Point;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class GuiObjectManager extends GuiScreen {
|
public class GuiObjectManager extends GuiScreen {
|
||||||
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
|
||||||
private KeyframeList[] keyframeLists;
|
|
||||||
|
|
||||||
private GuiEntryList<CustomImageObject> objectList;
|
private GuiEntryList<CustomImageObject> objectList;
|
||||||
private GuiAdvancedButton addButton, removeButton;
|
private GuiAdvancedButton addButton, removeButton;
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() {
|
GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
addKeyframe(line);
|
objectKeyframeTimeline.addKeyframe(line);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null);
|
null);
|
||||||
@@ -71,16 +72,14 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() {
|
GuiReplayOverlay.TEXTURE_SIZE, GuiReplayOverlay.TEXTURE_SIZE, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
removeKeyframe(line);
|
objectKeyframeTimeline.removeKeyframe(line);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null);
|
null);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GuiElement delegate() {
|
public GuiElement delegate() {
|
||||||
boolean sel = false; //TODO
|
if(objectKeyframeTimeline.getSelectedKeyframeRow() == line) {
|
||||||
|
|
||||||
if(sel) {
|
|
||||||
return selected;
|
return selected;
|
||||||
} else {
|
} else {
|
||||||
return normal;
|
return normal;
|
||||||
@@ -95,14 +94,6 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addKeyframe(int line) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeKeyframe(int line) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initGui() {
|
public void initGui() {
|
||||||
if(!initialized) {
|
if(!initialized) {
|
||||||
@@ -161,8 +152,6 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
nameInput = new GuiAdvancedTextField(fontRendererObj, 0, 0, 0, 20);
|
nameInput = new GuiAdvancedTextField(fontRendererObj, 0, 0, 0, 20);
|
||||||
nameInput.hint = I18n.format("replaymod.gui.objects.properties.name");
|
nameInput.hint = I18n.format("replaymod.gui.objects.properties.name");
|
||||||
|
|
||||||
keyframeLists = new KeyframeList[5];
|
|
||||||
|
|
||||||
objectList.addSelectionListener(new SelectionListener() {
|
objectList.addSelectionListener(new SelectionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSelectionChanged(int selectionIndex) {
|
public void onSelectionChanged(int selectionIndex) {
|
||||||
@@ -187,6 +176,7 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
|
|
||||||
assetDropdown.setSelectionIndexQuietly(sel);
|
assetDropdown.setSelectionIndexQuietly(sel);
|
||||||
|
|
||||||
|
objectKeyframeTimeline.setTransformations(selectedObject.getTransformations());
|
||||||
} else {
|
} else {
|
||||||
disableElements.setEnabled(false);
|
disableElements.setEnabled(false);
|
||||||
}
|
}
|
||||||
@@ -264,7 +254,7 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
int timelineWidth = this.width-10-timelineX + 2;
|
int timelineWidth = this.width-10-timelineX + 2;
|
||||||
int timelineHeight = this.height-10-10-timelineY + 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);
|
disableElements.addPart(objectKeyframeTimeline);
|
||||||
|
|
||||||
timelineScrollbar = new GuiScrollbar(timelineX, this.height-15, timelineWidth-21) {
|
timelineScrollbar = new GuiScrollbar(timelineX, this.height-15, timelineWidth-21) {
|
||||||
@@ -389,28 +379,107 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
|
|
||||||
public class GuiObjectKeyframeTimeline extends GuiTimeline {
|
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;
|
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);
|
super(x, y, width, height);
|
||||||
|
|
||||||
this.keyframeLists = keyframeLists;
|
|
||||||
|
|
||||||
this.zoom = 0.1;
|
this.zoom = 0.1;
|
||||||
this.timelineLength = 10 * 60 * 1000;
|
this.timelineLength = 10 * 60 * 1000;
|
||||||
this.showMarkers = true;
|
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<Keyframe>) 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
|
@Override
|
||||||
public boolean mouseClick(Minecraft mc, int mouseX, int mouseY, int button) {
|
public boolean mouseClick(Minecraft mc, int mouseX, int mouseY, int button) {
|
||||||
if(!enabled) return false;
|
if(!enabled) return false;
|
||||||
super.mouseClick(mc, mouseX, mouseY, button);
|
super.mouseClick(mc, mouseX, mouseY, button);
|
||||||
int time = (int) getTimeAt(mouseX, mouseY);
|
int time = (int) getTimeAt(mouseX, mouseY);
|
||||||
if(time != -1) {
|
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;
|
cursorPosition = time;
|
||||||
dragging = true;
|
dragging = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -435,5 +504,65 @@ public class GuiObjectManager extends GuiScreen {
|
|||||||
super.mouseRelease(mc, mouseX, mouseY, button);
|
super.mouseRelease(mc, mouseX, mouseY, button);
|
||||||
this.dragging = false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,4 @@ public class Transformation {
|
|||||||
private Position scale;
|
private Position scale;
|
||||||
private double opacity;
|
private double opacity;
|
||||||
|
|
||||||
private float width, height;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,43 @@ package eu.crushedpixel.replaymod.holders;
|
|||||||
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
|
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
public class Transformations {
|
public class Transformations {
|
||||||
|
|
||||||
private KeyframeList<Position> anchorKeyframes, positionKeyframes,
|
private KeyframeList<Position> anchorKeyframes = new KeyframeList<Position>();
|
||||||
orientationKeyframes, scaleKeyframes;
|
private KeyframeList<Position> positionKeyframes = new KeyframeList<Position>();
|
||||||
private KeyframeList<NumberValue> opacityKeyframes;
|
private KeyframeList<Position> orientationKeyframes = new KeyframeList<Position>();
|
||||||
|
private KeyframeList<Position> scaleKeyframes = new KeyframeList<Position>();
|
||||||
|
private KeyframeList<NumberValue> opacityKeyframes = new KeyframeList<NumberValue>();
|
||||||
|
|
||||||
public Transformation getTransformationForTimestamp(int timestamp) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transformation getTransformationForTimestamp(int timestamp) {
|
||||||
|
return new Transformation(
|
||||||
|
anchorKeyframes.getInterpolatedValueForTimestamp(timestamp, true),
|
||||||
|
positionKeyframes.getInterpolatedValueForTimestamp(timestamp, true),
|
||||||
|
orientationKeyframes.getInterpolatedValueForTimestamp(timestamp, true),
|
||||||
|
scaleKeyframes.getInterpolatedValueForTimestamp(timestamp, true),
|
||||||
|
opacityKeyframes.getInterpolatedValueForTimestamp(timestamp, true).value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.crushedpixel.replaymod.interpolation;
|
package eu.crushedpixel.replaymod.interpolation;
|
||||||
|
|
||||||
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
|
|
||||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||||
import eu.crushedpixel.replaymod.holders.KeyframeComparator;
|
import eu.crushedpixel.replaymod.holders.KeyframeComparator;
|
||||||
|
|
||||||
@@ -108,7 +107,6 @@ public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>
|
|||||||
public Keyframe<K> getClosestKeyframeForTimestamp(int realTime, int tolerance) {
|
public Keyframe<K> getClosestKeyframeForTimestamp(int realTime, int tolerance) {
|
||||||
List<Keyframe<K>> found = new ArrayList<Keyframe<K>>();
|
List<Keyframe<K>> found = new ArrayList<Keyframe<K>>();
|
||||||
for(Keyframe<K> kf : this) {
|
for(Keyframe<K> kf : this) {
|
||||||
if(!(kf.getValue() instanceof AdvancedPosition)) continue;
|
|
||||||
if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) {
|
if(Math.abs(kf.getRealTimestamp() - realTime) <= tolerance) {
|
||||||
found.add(kf);
|
found.add(kf);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user