Created CustomObjectRepository which can be serialized by Gson, storing all CustomImageObjects created
CustomObjectRepository is now being saved to and loaded from the ReplayFile The CustomObjectRenderer uses the GuiObjectManager's timeline cursor position as interpolation timestamp for the CustomImageObjects which are drawn if a GuiObjectManager is open (instant preview) KeyframeList's add() method now removes Keyframes with the same timestamp before adding the new Keyframe Transformations class now holds default values to prevent NullPointerExceptions in methods calling getTransformationForTimestamp() Added Javadoc to Transformation POJO Created NumberValueChangeListener which can be applied to GuiNumberInput objects Made step size customizable in GuiDraggingNumberInput Changing a ReplayImageAssets image now notifies the CustomImageObjects linked to that asset Modified RoundUtils to provide a round() method which uses the DecimalFormat class instead of Math.round which is unstable
This commit is contained in:
@@ -27,8 +27,8 @@ public class CustomImageObject implements GuiEntryListEntry {
|
||||
|
||||
@Getter @Setter private float width, height;
|
||||
|
||||
private ResourceLocation resourceLocation;
|
||||
private DynamicTexture dynamicTexture;
|
||||
private transient ResourceLocation resourceLocation;
|
||||
private transient DynamicTexture dynamicTexture;
|
||||
|
||||
@Getter private float textureWidth, textureHeight;
|
||||
|
||||
@@ -38,6 +38,7 @@ public class CustomImageObject implements GuiEntryListEntry {
|
||||
ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(assetUUID);
|
||||
|
||||
if(asset instanceof ReplayImageAsset) {
|
||||
this.linkedAsset = assetUUID;
|
||||
setImage(((ReplayImageAsset)asset).getObject());
|
||||
} else if(asset != null) {
|
||||
throw new UnsupportedOperationException("A CustomImageObject requires a ReplayImageAsset");
|
||||
@@ -63,16 +64,30 @@ public class CustomImageObject implements GuiEntryListEntry {
|
||||
this.setWidth(w);
|
||||
this.setHeight(h);
|
||||
|
||||
resourceLocation = new ResourceLocation(linkedAsset.toString());
|
||||
|
||||
Minecraft.getMinecraft().addScheduledTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
resourceLocation = new ResourceLocation("customImages/"+linkedAsset.toString());
|
||||
dynamicTexture = new DynamicTexture(bufferedImage);
|
||||
|
||||
ResourceHelper.freeResource(resourceLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ResourceLocation getResourceLocation() {
|
||||
if(resourceLocation == null) {
|
||||
ReplayAsset asset = ReplayHandler.getAssetRepository().getAssetByUUID(linkedAsset);
|
||||
if(asset instanceof ReplayImageAsset) {
|
||||
try {
|
||||
setImage(((ReplayImageAsset) asset).getObject());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!ResourceHelper.isRegistered(resourceLocation)) {
|
||||
ResourceHelper.registerResource(resourceLocation);
|
||||
Minecraft.getMinecraft().getTextureManager().loadTexture(resourceLocation, dynamicTexture);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package eu.crushedpixel.replaymod.assets;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CustomObjectRepository {
|
||||
|
||||
public CustomObjectRepository() {
|
||||
this.objects = new ArrayList<CustomImageObject>();
|
||||
}
|
||||
|
||||
private ArrayList<CustomImageObject> objects;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.crushedpixel.replaymod.assets;
|
||||
|
||||
import eu.crushedpixel.replaymod.registry.ResourceHelper;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.utils.BoundingUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
@@ -46,6 +47,12 @@ public class ReplayImageAsset implements ReplayAsset<BufferedImage> {
|
||||
public void loadFromStream(InputStream inputStream) throws IOException {
|
||||
this.object = ImageIO.read(inputStream);
|
||||
ResourceHelper.freeResource(previewResource);
|
||||
|
||||
for(CustomImageObject object : ReplayHandler.getCustomImageObjects()) {
|
||||
if(object.getLinkedAsset().equals(ReplayHandler.getAssetRepository().getUUIDForAsset(this))) {
|
||||
object.setImage(this.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.assets.CustomImageObject;
|
||||
import eu.crushedpixel.replaymod.assets.CustomObjectRepository;
|
||||
import eu.crushedpixel.replaymod.assets.ReplayAsset;
|
||||
import eu.crushedpixel.replaymod.gui.elements.*;
|
||||
import eu.crushedpixel.replaymod.gui.elements.listeners.NumberValueChangeListener;
|
||||
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
||||
import eu.crushedpixel.replaymod.gui.elements.timelines.GuiTimeline;
|
||||
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
|
||||
@@ -10,6 +13,8 @@ import eu.crushedpixel.replaymod.holders.*;
|
||||
import eu.crushedpixel.replaymod.interpolation.KeyframeList;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.utils.MouseUtils;
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFile;
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -19,10 +24,14 @@ import net.minecraft.client.resources.I18n;
|
||||
import org.lwjgl.util.Point;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.lwjgl.util.Point;
|
||||
|
||||
public class GuiObjectManager extends GuiScreen {
|
||||
|
||||
private boolean initialized = false;
|
||||
@@ -37,11 +46,15 @@ public class GuiObjectManager extends GuiScreen {
|
||||
|
||||
private GuiDraggingNumberInput anchorXInput, anchorYInput, anchorZInput;
|
||||
private GuiDraggingNumberInput positionXInput, positionYInput, positionZInput;
|
||||
private GuiDraggingNumberInput scaleXInput, scaleYInput, scaleZInput;
|
||||
private GuiDraggingNumberInput orientationXInput, orientationYInput, orientationZInput;
|
||||
private GuiDraggingNumberInput scaleXInput, scaleYInput, scaleZInput;
|
||||
private GuiDraggingNumberInput opacityInput;
|
||||
|
||||
private NumberInputGroup anchorNumberInputs, positionNumberInputs, orientationNumberInputs, scaleNumberInputs, opacityNumberInputs;
|
||||
|
||||
@Getter
|
||||
private GuiObjectKeyframeTimeline objectKeyframeTimeline;
|
||||
|
||||
private GuiScrollbar timelineScrollbar;
|
||||
|
||||
private GuiTexturedButton zoomInButton, zoomOutButton;
|
||||
@@ -97,29 +110,35 @@ public class GuiObjectManager extends GuiScreen {
|
||||
@Override
|
||||
public void initGui() {
|
||||
if(!initialized) {
|
||||
anchorXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
anchorYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
anchorZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
anchorXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true, "", 0.1);
|
||||
anchorYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true, "", 0.1);
|
||||
anchorZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true, "", 0.1);
|
||||
anchorNumberInputs = new NumberPositionInputGroup(null, anchorXInput, anchorYInput, anchorZInput);
|
||||
|
||||
positionXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
positionYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
positionZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
|
||||
scaleXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%");
|
||||
scaleYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%");
|
||||
scaleZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%");
|
||||
positionNumberInputs = new NumberPositionInputGroup(null, positionXInput, positionYInput, positionZInput);
|
||||
|
||||
orientationXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
orientationYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
orientationZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 0d, true);
|
||||
orientationNumberInputs = new NumberPositionInputGroup(null, orientationXInput, orientationYInput, orientationZInput);
|
||||
|
||||
opacityInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, 0d, 100d, 100d, true, "%");
|
||||
scaleXInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%", 0.5);
|
||||
scaleYInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%", 0.5);
|
||||
scaleZInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, null, null, 100d, true, "%", 0.5);
|
||||
scaleNumberInputs = new NumberPositionInputGroup(null, scaleXInput, scaleYInput, scaleZInput);
|
||||
|
||||
opacityInput = new GuiDraggingNumberInput(fontRendererObj, 0, 0, 50, 0d, 100d, 100d, true, "%", 0.5);
|
||||
opacityNumberInputs = new NumberValueInputGroup(null, opacityInput);
|
||||
|
||||
dropdownLabel = new GuiString(0, 0, Color.WHITE, I18n.format("replaymod.gui.assets.filechooser")+": ");
|
||||
assetDropdown = new GuiDropdown<GuiEntryListValueEntry<UUID>>(fontRendererObj, 0, 0, 0, 5);
|
||||
if(ReplayHandler.getAssetRepository().getCopyOfReplayAssets().isEmpty()) {
|
||||
assetDropdown.addElement(new GuiEntryListValueEntry<UUID>(I18n.format("replaymod.gui.assets.emptylist"), null));
|
||||
} else {
|
||||
assetDropdown.addElement(new GuiEntryListValueEntry<UUID>(I18n.format("replaymod.gui.assets.noselection"), null));
|
||||
for(ReplayAsset asset : ReplayHandler.getAssetRepository().getCopyOfReplayAssets()) {
|
||||
assetDropdown.addElement(new GuiEntryListValueEntry<UUID>(
|
||||
asset.getDisplayString(), ReplayHandler.getAssetRepository().getUUIDForAsset(asset)));
|
||||
@@ -152,6 +171,10 @@ public class GuiObjectManager extends GuiScreen {
|
||||
nameInput = new GuiAdvancedTextField(fontRendererObj, 0, 0, 0, 20);
|
||||
nameInput.hint = I18n.format("replaymod.gui.objects.properties.name");
|
||||
|
||||
for(CustomImageObject customImageObject : ReplayHandler.getCustomImageObjects()) {
|
||||
objectList.addElement(customImageObject);
|
||||
}
|
||||
|
||||
objectList.addSelectionListener(new SelectionListener() {
|
||||
@Override
|
||||
public void onSelectionChanged(int selectionIndex) {
|
||||
@@ -176,7 +199,17 @@ public class GuiObjectManager extends GuiScreen {
|
||||
|
||||
assetDropdown.setSelectionIndexQuietly(sel);
|
||||
|
||||
objectKeyframeTimeline.setTransformations(selectedObject.getTransformations());
|
||||
Transformations transformations = selectedObject.getTransformations();
|
||||
|
||||
objectKeyframeTimeline.setTransformations(transformations);
|
||||
|
||||
anchorNumberInputs.setUnderlyingKeyframeList(transformations.getAnchorKeyframes());
|
||||
positionNumberInputs.setUnderlyingKeyframeList(transformations.getPositionKeyframes());
|
||||
orientationNumberInputs.setUnderlyingKeyframeList(transformations.getOrientationKeyframes());
|
||||
scaleNumberInputs.setUnderlyingKeyframeList(transformations.getScaleKeyframes());
|
||||
opacityNumberInputs.setUnderlyingKeyframeList(transformations.getOpacityKeyframes());
|
||||
|
||||
updateValuesForTransformation(objectKeyframeTimeline.getTransformations().getTransformationForTimestamp(objectKeyframeTimeline.cursorPosition));
|
||||
} else {
|
||||
disableElements.setEnabled(false);
|
||||
}
|
||||
@@ -202,8 +235,8 @@ public class GuiObjectManager extends GuiScreen {
|
||||
String[] labelStrings = new String[] {
|
||||
I18n.format("replaymod.gui.objects.properties.anchor"),
|
||||
I18n.format("replaymod.gui.objects.properties.position"),
|
||||
I18n.format("replaymod.gui.objects.properties.scale"),
|
||||
I18n.format("replaymod.gui.objects.properties.orientation"),
|
||||
I18n.format("replaymod.gui.objects.properties.scale"),
|
||||
I18n.format("replaymod.gui.objects.properties.opacity"),
|
||||
};
|
||||
|
||||
@@ -222,10 +255,10 @@ public class GuiObjectManager extends GuiScreen {
|
||||
|
||||
anchorInputs = new ComposedElement(anchorXInput, anchorYInput, anchorZInput);
|
||||
positionInputs = new ComposedElement(positionXInput, positionYInput, positionZInput);
|
||||
scaleInputs = new ComposedElement(scaleXInput, scaleYInput, scaleZInput);
|
||||
orientationInputs = new ComposedElement(orientationXInput, orientationYInput, orientationZInput);
|
||||
scaleInputs = new ComposedElement(scaleXInput, scaleYInput, scaleZInput);
|
||||
opacityInputs = new ComposedElement(opacityInput);
|
||||
numberInputs = new ComposedElement(anchorInputs, positionInputs, scaleInputs, orientationInputs, opacityInputs);
|
||||
numberInputs = new ComposedElement(anchorInputs, positionInputs, orientationInputs, scaleInputs, opacityInputs);
|
||||
|
||||
for(int i = numberInputs.getParts().size()-1; i >= 0; i--) {
|
||||
int yPos = this.height-5-10-(25*(numberInputs.getParts().size()-i));
|
||||
@@ -329,6 +362,43 @@ public class GuiObjectManager extends GuiScreen {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void saveOnQuit() {
|
||||
ArrayList<CustomImageObject> objects = objectList.getCopyOfElements();
|
||||
ReplayHandler.setCustomImageObjects(objects);
|
||||
|
||||
if(objects.size() > 0) {
|
||||
try {
|
||||
File f = File.createTempFile(ReplayFile.ENTRY_CUSTOM_OBJECTS, "json");
|
||||
ReplayFileIO.write(new CustomObjectRepository(objects), f);
|
||||
ReplayMod.replayFileAppender.registerModifiedFile(f, ReplayFile.ENTRY_CUSTOM_OBJECTS, ReplayHandler.getReplayFile());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
ReplayMod.replayFileAppender.registerModifiedFile(null, ReplayFile.ENTRY_CUSTOM_OBJECTS, ReplayHandler.getReplayFile());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateValuesForTransformation(Transformation transformation) {
|
||||
anchorXInput.setValueQuietly(transformation.getAnchor().getX());
|
||||
anchorYInput.setValueQuietly(transformation.getAnchor().getY());
|
||||
anchorZInput.setValueQuietly(transformation.getAnchor().getZ());
|
||||
|
||||
positionXInput.setValueQuietly(transformation.getPosition().getX());
|
||||
positionYInput.setValueQuietly(transformation.getPosition().getY());
|
||||
positionZInput.setValueQuietly(transformation.getPosition().getZ());
|
||||
|
||||
orientationXInput.setValueQuietly(transformation.getOrientation().getX());
|
||||
orientationYInput.setValueQuietly(transformation.getOrientation().getY());
|
||||
orientationZInput.setValueQuietly(transformation.getOrientation().getZ());
|
||||
|
||||
scaleXInput.setValueQuietly(transformation.getScale().getX());
|
||||
scaleYInput.setValueQuietly(transformation.getScale().getY());
|
||||
scaleZInput.setValueQuietly(transformation.getScale().getZ());
|
||||
|
||||
opacityInput.setValueQuietly(transformation.getOpacity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
this.drawDefaultBackground();
|
||||
@@ -377,6 +447,70 @@ public class GuiObjectManager extends GuiScreen {
|
||||
super.mouseReleased(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
saveOnQuit();
|
||||
}
|
||||
|
||||
public abstract class NumberInputGroup implements NumberValueChangeListener {
|
||||
|
||||
KeyframeList toModify;
|
||||
|
||||
public void setUnderlyingKeyframeList(KeyframeList toModify) {
|
||||
this.toModify = toModify;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class NumberValueInputGroup extends NumberInputGroup {
|
||||
|
||||
public NumberValueInputGroup(KeyframeList<NumberValue> toModify, GuiNumberInput input) {
|
||||
this.toModify = toModify;
|
||||
|
||||
input.addValueChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValueChange(double value) {
|
||||
NumberValue numberValue = new NumberValue(value);
|
||||
|
||||
//if keyframe selected, overwrite its value
|
||||
if(toModify.contains(objectKeyframeTimeline.getSelectedKeyframe())) {
|
||||
objectKeyframeTimeline.getSelectedKeyframe().setValue(numberValue);
|
||||
} else {
|
||||
toModify.add(new Keyframe<NumberValue>(objectKeyframeTimeline.cursorPosition, numberValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NumberPositionInputGroup extends NumberInputGroup {
|
||||
|
||||
public NumberPositionInputGroup(KeyframeList<Position> toModify, GuiNumberInput xInput, GuiNumberInput yInput, GuiNumberInput zInput) {
|
||||
this.toModify = toModify;
|
||||
this.xInput = xInput;
|
||||
this.yInput = yInput;
|
||||
this.zInput = zInput;
|
||||
|
||||
xInput.addValueChangeListener(this);
|
||||
yInput.addValueChangeListener(this);
|
||||
zInput.addValueChangeListener(this);
|
||||
}
|
||||
|
||||
private GuiNumberInput xInput, yInput, zInput;
|
||||
|
||||
@Override
|
||||
public void onValueChange(double value) {
|
||||
Position position = new Position(xInput.getPreciseValue(), yInput.getPreciseValue(), zInput.getPreciseValue());
|
||||
|
||||
//if keyframe selected, overwrite its value
|
||||
if(toModify.contains(objectKeyframeTimeline.getSelectedKeyframe())) {
|
||||
objectKeyframeTimeline.getSelectedKeyframe().setValue(position);
|
||||
} else {
|
||||
toModify.add(new Keyframe<Position>(objectKeyframeTimeline.cursorPosition, position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GuiObjectKeyframeTimeline extends GuiTimeline {
|
||||
|
||||
private static final int KEYFRAME_X = 74;
|
||||
@@ -481,6 +615,8 @@ public class GuiObjectManager extends GuiScreen {
|
||||
}
|
||||
|
||||
cursorPosition = time;
|
||||
updateValuesForTransformation(getTransformations().getTransformationForTimestamp(cursorPosition));
|
||||
|
||||
dragging = true;
|
||||
return true;
|
||||
}
|
||||
@@ -494,7 +630,10 @@ public class GuiObjectManager extends GuiScreen {
|
||||
super.mouseDrag(mc, mouseX, mouseY, button);
|
||||
if(dragging) {
|
||||
int time = (int) getTimeAt(mouseX, mouseY);
|
||||
if(time != -1) cursorPosition = time;
|
||||
if(time != -1) {
|
||||
cursorPosition = time;
|
||||
updateValuesForTransformation(getTransformations().getTransformationForTimestamp(cursorPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,23 @@ public class GuiDraggingNumberInput extends GuiNumberInputWithText {
|
||||
|
||||
public GuiDraggingNumberInput(FontRenderer fontRenderer,
|
||||
int xPos, int yPos, int width, Double minimum, Double maximum, Double defaultValue, boolean acceptFloats) {
|
||||
this(fontRenderer, xPos, yPos, width, minimum, maximum, defaultValue, acceptFloats, "");
|
||||
this(fontRenderer, xPos, yPos, width, minimum, maximum, defaultValue, acceptFloats, "", 0.5f);
|
||||
}
|
||||
|
||||
public GuiDraggingNumberInput(FontRenderer fontRenderer,
|
||||
int xPos, int yPos, int width, Double minimum,
|
||||
Double maximum, Double defaultValue, boolean acceptFloats, String suffix) {
|
||||
Double maximum, Double defaultValue, boolean acceptFloats, String suffix, double stepSize) {
|
||||
super(fontRenderer, xPos, yPos, width, minimum, maximum, defaultValue, acceptFloats, suffix);
|
||||
|
||||
this.stepSize = stepSize;
|
||||
}
|
||||
|
||||
private int prevMouseX;
|
||||
private boolean dragging;
|
||||
private boolean clicked;
|
||||
|
||||
private double stepSize;
|
||||
|
||||
@Override
|
||||
public boolean mouseClick(Minecraft mc, int mouseX, int mouseY, int button) {
|
||||
if(MouseUtils.isMouseWithinBounds(xPosition, yPosition, width, height) && isEnabled) {
|
||||
@@ -40,16 +44,10 @@ public class GuiDraggingNumberInput extends GuiNumberInputWithText {
|
||||
int diff = mouseX - prevMouseX;
|
||||
prevMouseX = mouseX;
|
||||
|
||||
int bounds = 100;
|
||||
|
||||
if(minimum != null && maximum != null) {
|
||||
bounds = (int)(maximum-minimum);
|
||||
}
|
||||
|
||||
double value = getPreciseValue();
|
||||
double valueDiff = RoundUtils.round2Decimals((diff / 200f) * bounds);
|
||||
double valueDiff = diff * stepSize;
|
||||
|
||||
this.setValue(value+valueDiff);
|
||||
this.setValue(RoundUtils.round2Decimals(value + valueDiff));
|
||||
}
|
||||
super.mouseDrag(mc, mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class GuiEntryList<T extends GuiEntryListEntry> extends GuiAdvancedTextFi
|
||||
fireSelectionChangeEvent();
|
||||
}
|
||||
|
||||
public List<T> getCopyOfElements() {
|
||||
public ArrayList<T> getCopyOfElements() {
|
||||
return new ArrayList<T>(elements);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package eu.crushedpixel.replaymod.gui.elements;
|
||||
|
||||
import eu.crushedpixel.replaymod.gui.elements.listeners.NumberValueChangeListener;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiNumberInput extends GuiAdvancedTextField {
|
||||
|
||||
private List<NumberValueChangeListener> valueChangeListeners = new ArrayList<NumberValueChangeListener>();
|
||||
|
||||
protected Double minimum, maximum;
|
||||
|
||||
protected boolean acceptFloats = false;
|
||||
@@ -25,7 +31,11 @@ public class GuiNumberInput extends GuiAdvancedTextField {
|
||||
this(fontRenderer, xPos, yPos, width, (double) minimum, (double) maximum, (double) defaultValue, acceptFloats);
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
/**
|
||||
* Sets this GuiNumberInput's value without notifying the listeners.
|
||||
* @param value The value to set
|
||||
*/
|
||||
public void setValueQuietly(double value) {
|
||||
if(minimum != null && value < minimum) {
|
||||
setText(acceptFloats ? minimum.toString() : Integer.toString((int) Math.round(minimum)));
|
||||
} else if(maximum != null && value > maximum) {
|
||||
@@ -40,6 +50,22 @@ public class GuiNumberInput extends GuiAdvancedTextField {
|
||||
setCursorPositionZero();
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
setValueQuietly(value);
|
||||
fireValueChangeEvent();
|
||||
}
|
||||
|
||||
public void addValueChangeListener(NumberValueChangeListener listener) {
|
||||
this.valueChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
private void fireValueChangeEvent() {
|
||||
double val = acceptFloats ? getPreciseValue() : getIntValue();
|
||||
for(NumberValueChangeListener listener : valueChangeListeners) {
|
||||
listener.onValueChange(val);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeText(String text) {
|
||||
String textBefore = getText();
|
||||
@@ -59,6 +85,8 @@ public class GuiNumberInput extends GuiAdvancedTextField {
|
||||
} else if(maximum != null && val > maximum) {
|
||||
setText(acceptFloats ? maximum.toString() : Integer.toString((int) Math.round(maximum)));
|
||||
}
|
||||
|
||||
fireValueChangeEvent();
|
||||
} catch(NumberFormatException e) {
|
||||
setText(textBefore);
|
||||
setCursorPosition(cursorPositionBefore);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package eu.crushedpixel.replaymod.gui.elements.listeners;
|
||||
|
||||
public interface NumberValueChangeListener {
|
||||
|
||||
void onValueChange(double value);
|
||||
|
||||
}
|
||||
@@ -7,10 +7,30 @@ import lombok.Data;
|
||||
@AllArgsConstructor
|
||||
public class Transformation {
|
||||
|
||||
/**
|
||||
* The object's anchor around which modifiers like orientation or position should be applied.
|
||||
* x=0, y=0, z=0 is the object's center.
|
||||
*/
|
||||
private Position anchor;
|
||||
|
||||
/**
|
||||
* The object's position in the Minecraft world.
|
||||
*/
|
||||
private Position position;
|
||||
|
||||
/**
|
||||
* The object's orientation.
|
||||
*/
|
||||
private Position orientation;
|
||||
|
||||
/**
|
||||
* The object's scale, individual values between 0 and 100.
|
||||
*/
|
||||
private Position scale;
|
||||
|
||||
/**
|
||||
* The object's opacity. Value between 0 and 100.
|
||||
*/
|
||||
private double opacity;
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor
|
||||
public class Transformations {
|
||||
|
||||
private Position defaultAnchor = new Position(0, 0, 0),
|
||||
defaultPosition = new Position(0, 0, 0),
|
||||
defaultOrientation = new Position(0, 0, 0),
|
||||
defaultScale = new Position(100, 100, 100);
|
||||
private NumberValue defaultOpacity = new NumberValue(100);
|
||||
|
||||
private KeyframeList<Position> anchorKeyframes = new KeyframeList<Position>();
|
||||
private KeyframeList<Position> positionKeyframes = new KeyframeList<Position>();
|
||||
private KeyframeList<Position> orientationKeyframes = new KeyframeList<Position>();
|
||||
@@ -34,12 +40,22 @@ public class Transformations {
|
||||
}
|
||||
|
||||
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);
|
||||
Position anchor = anchorKeyframes.getInterpolatedValueForTimestamp(timestamp, true);
|
||||
if(anchor == null) anchor = defaultAnchor;
|
||||
|
||||
Position position = positionKeyframes.getInterpolatedValueForTimestamp(timestamp, true);
|
||||
if(position == null) position = defaultPosition;
|
||||
|
||||
Position orientation = orientationKeyframes.getInterpolatedValueForTimestamp(timestamp, true);
|
||||
if(orientation == null) orientation = defaultOrientation;
|
||||
|
||||
Position scale = scaleKeyframes.getInterpolatedValueForTimestamp(timestamp, true);
|
||||
if(scale == null) scale = defaultScale;
|
||||
|
||||
NumberValue opacity = opacityKeyframes.getInterpolatedValueForTimestamp(timestamp, true);
|
||||
if(opacity == null) opacity = defaultOpacity;
|
||||
|
||||
return new Transformation(anchor, position, orientation, scale, opacity.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,13 @@ public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>
|
||||
|
||||
@Override
|
||||
public boolean add(Keyframe<K> t) {
|
||||
//remove keyframes that have same timestamp
|
||||
for(Keyframe kf : new ArrayList<Keyframe>(this)) {
|
||||
if(kf.getRealTimestamp() == t.getRealTimestamp()) {
|
||||
super.remove(kf);
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = super.add(t);
|
||||
sort();
|
||||
return success;
|
||||
@@ -137,6 +144,9 @@ public class KeyframeList<K extends KeyframeValue> extends ArrayList<Keyframe<K>
|
||||
}
|
||||
|
||||
public K getInterpolatedValueForPathPosition(float pathPosition, boolean linear) {
|
||||
if(first() == null) return null;
|
||||
if(size() == 1) return first().getValue();
|
||||
|
||||
K toApply = (K)first().getValue().newInstance();
|
||||
|
||||
if(previousCallLinear != (Boolean)linear) {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package eu.crushedpixel.replaymod.renderer;
|
||||
|
||||
import eu.crushedpixel.replaymod.assets.CustomImageObject;
|
||||
import eu.crushedpixel.replaymod.gui.GuiObjectManager;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.holders.Transformation;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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.
|
||||
@@ -19,12 +25,9 @@ public class CustomObjectRenderer {
|
||||
public void renderCustomObjects(RenderWorldLastEvent event) {
|
||||
if(!ReplayHandler.isInReplay()) return;
|
||||
|
||||
Entity entity = ReplayHandler.getCameraEntity();
|
||||
if(entity == null) return;
|
||||
|
||||
double doubleX = entity.posX;
|
||||
double doubleY = entity.posY;
|
||||
double doubleZ = entity.posZ;
|
||||
double dX = mc.getRenderViewEntity().lastTickPosX + (mc.getRenderViewEntity().posX - mc.getRenderViewEntity().lastTickPosX) * (double)event.partialTicks;
|
||||
double dY = mc.getRenderViewEntity().lastTickPosY + (mc.getRenderViewEntity().posY - mc.getRenderViewEntity().lastTickPosY) * (double)event.partialTicks;
|
||||
double dZ = mc.getRenderViewEntity().lastTickPosZ + (mc.getRenderViewEntity().posZ - mc.getRenderViewEntity().lastTickPosZ) * (double)event.partialTicks;
|
||||
|
||||
GlStateManager.pushAttrib();
|
||||
GlStateManager.pushMatrix();
|
||||
@@ -35,7 +38,7 @@ public class CustomObjectRenderer {
|
||||
GlStateManager.disableTexture2D();
|
||||
|
||||
for(CustomImageObject object : ReplayHandler.getCustomImageObjects()) {
|
||||
drawCustomImageObject(doubleX, doubleY, doubleZ, object);
|
||||
drawCustomImageObject(dX, dY, dZ, object);
|
||||
}
|
||||
|
||||
GlStateManager.enableTexture2D();
|
||||
@@ -47,8 +50,12 @@ public class CustomObjectRenderer {
|
||||
}
|
||||
|
||||
private void drawCustomImageObject(double playerX, double playerY, double playerZ, CustomImageObject customImageObject) {
|
||||
//TODO: Get current Transformations for given Timestamp
|
||||
/*
|
||||
ResourceLocation resourceLocation = customImageObject.getResourceLocation();
|
||||
|
||||
if(customImageObject.getLinkedAsset() == null
|
||||
|| ReplayHandler.getAssetRepository().getAssetByUUID(customImageObject.getLinkedAsset()) == null
|
||||
|| resourceLocation == null) return;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
@@ -60,9 +67,20 @@ public class CustomObjectRenderer {
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
WorldRenderer renderer = tessellator.getWorldRenderer();
|
||||
|
||||
mc.renderEngine.bindTexture(customImageObject.getResourceLocation());
|
||||
mc.renderEngine.bindTexture(resourceLocation);
|
||||
|
||||
Transformations objectPosition = customImageObject.getPosition();
|
||||
int renderTimestamp;
|
||||
if(mc.currentScreen instanceof GuiObjectManager) {
|
||||
renderTimestamp = ((GuiObjectManager)mc.currentScreen).getObjectKeyframeTimeline().cursorPosition;
|
||||
} else {
|
||||
renderTimestamp = ReplayHandler.getRealTimelineCursor();
|
||||
}
|
||||
|
||||
Transformation transformation = customImageObject.getTransformations().getTransformationForTimestamp(renderTimestamp);
|
||||
|
||||
Position objectAnchor = transformation.getAnchor();
|
||||
Position objectPosition = transformation.getPosition();
|
||||
Position objectOrientation = transformation.getOrientation();
|
||||
|
||||
double x = objectPosition.getX() - playerX;
|
||||
double y = objectPosition.getY() - playerY;
|
||||
@@ -72,23 +90,23 @@ public class CustomObjectRenderer {
|
||||
|
||||
GlStateManager.translate(x, y + 1.4, z);
|
||||
|
||||
GlStateManager.rotate(-objectPosition.getYaw(), 0, 1, 0);
|
||||
GlStateManager.rotate(objectPosition.getRoll(), 0, 0, 1);
|
||||
GlStateManager.rotate(objectPosition.getPitch(), 1, 0, 0);
|
||||
GlStateManager.rotate((float)-objectOrientation.getX(), 0, 1, 0);
|
||||
GlStateManager.rotate((float)objectOrientation.getY(), 0, 0, 1);
|
||||
GlStateManager.rotate((float) objectOrientation.getZ(), 1, 0, 0);
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
float opacity = objectPosition.getOpacity();
|
||||
float opacity = (float)transformation.getOpacity() / 100;
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
float width = objectPosition.getWidth() * objectPosition.getScale();
|
||||
float height = objectPosition.getHeight() * objectPosition.getScale();
|
||||
float width = (float)(customImageObject.getWidth() * transformation.getScale().getX() / 100f);
|
||||
float height = (float)(customImageObject.getHeight() * transformation.getScale().getY() / 100f);
|
||||
|
||||
float minX = -width/2 + objectPosition.getAnchorX();
|
||||
float maxX = width/2 + objectPosition.getAnchorX();
|
||||
float minY = -height/2 + objectPosition.getAnchorY();
|
||||
float maxY = height/2 + objectPosition.getAnchorY();
|
||||
float minX = (float)(-width/2 + objectAnchor.getX());
|
||||
float maxX = (float)(width/2 + objectAnchor.getX());
|
||||
float minY = (float)(-height/2 + objectAnchor.getY());
|
||||
float maxY = (float)(height/2 + objectAnchor.getY());
|
||||
|
||||
renderer.startDrawingQuads();
|
||||
|
||||
@@ -109,6 +127,5 @@ public class CustomObjectRenderer {
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.popMatrix();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.assets.CustomObjectRepository;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.events.KeyframesModifyEvent;
|
||||
import eu.crushedpixel.replaymod.events.ReplayExitEvent;
|
||||
@@ -68,7 +69,7 @@ public class ReplayHandler {
|
||||
@Getter @Setter
|
||||
private static AssetRepository assetRepository;
|
||||
|
||||
private static List<CustomImageObject> customImageObjects = new ArrayList<CustomImageObject>();
|
||||
private static CustomObjectRepository customImageObjects = new CustomObjectRepository();
|
||||
|
||||
/**
|
||||
* The file currently being played.
|
||||
@@ -455,6 +456,11 @@ public class ReplayHandler {
|
||||
AssetRepository assets = currentReplayFile.assetRepository().get();
|
||||
assetRepository = assets;
|
||||
|
||||
//load custom image objects
|
||||
CustomObjectRepository objectRepository = currentReplayFile.customImageObjects().get();
|
||||
customImageObjects = objectRepository;
|
||||
if(customImageObjects == null) customImageObjects = new CustomObjectRepository();
|
||||
|
||||
ReplayMod.replaySender = new ReplaySender(currentReplayFile, asyncMode);
|
||||
channel.pipeline().addFirst(ReplayMod.replaySender);
|
||||
channel.pipeline().fireChannelActive();
|
||||
@@ -604,15 +610,15 @@ public class ReplayHandler {
|
||||
}
|
||||
|
||||
public static List<CustomImageObject> getCustomImageObjects() {
|
||||
return customImageObjects;
|
||||
return customImageObjects.getObjects();
|
||||
}
|
||||
|
||||
public static void addCustomImageObject(CustomImageObject object) {
|
||||
customImageObjects.add(object);
|
||||
customImageObjects.getObjects().add(object);
|
||||
}
|
||||
|
||||
public static void setCustomImageObjects(List<CustomImageObject> objects) {
|
||||
customImageObjects = objects;
|
||||
public static void setCustomImageObjects(ArrayList<CustomImageObject> objects) {
|
||||
customImageObjects.setObjects(objects);
|
||||
}
|
||||
|
||||
public static void fireKeyframesModifyEvent() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import eu.crushedpixel.replaymod.assets.AssetRepository;
|
||||
import eu.crushedpixel.replaymod.assets.CustomObjectRepository;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||
import eu.crushedpixel.replaymod.holders.MarkerKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
|
||||
@@ -31,11 +32,12 @@ public class ReplayFile extends ZipFile {
|
||||
public static final String ENTRY_PATHS = "paths" + JSON_FILE_EXTENSION;
|
||||
public static final String ENTRY_THUMB = "thumb";
|
||||
public static final String ENTRY_RESOURCE_PACK = "resourcepack/%s.zip";
|
||||
public static final String ENTRY_RESOURCE_PACK_INDEX = "resourcepack/index.json";
|
||||
public static final String ENTRY_RESOURCE_PACK_INDEX = "resourcepack/index"+JSON_FILE_EXTENSION;
|
||||
public static final String ENTRY_VISIBILITY_OLD = "visibility";
|
||||
public static final String ENTRY_VISIBILITY = "visibility" + JSON_FILE_EXTENSION;
|
||||
public static final String ENTRY_MARKERS = "markers" + JSON_FILE_EXTENSION;
|
||||
public static final String ENTRY_ASSET_FOLDER = "asset/";
|
||||
public static final String ENTRY_CUSTOM_OBJECTS = "objects"+JSON_FILE_EXTENSION;
|
||||
|
||||
private final File file;
|
||||
|
||||
@@ -258,4 +260,27 @@ public class ReplayFile extends ZipFile {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ZipArchiveEntry customObjectsEntry() {
|
||||
return getEntry(ENTRY_CUSTOM_OBJECTS);
|
||||
}
|
||||
|
||||
public Supplier<CustomObjectRepository> customImageObjects() {
|
||||
return new Supplier<CustomObjectRepository>() {
|
||||
@Override
|
||||
public CustomObjectRepository get() {
|
||||
try {
|
||||
ZipArchiveEntry entry = customObjectsEntry();
|
||||
if(entry == null) return null;
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(getInputStream(entry)));
|
||||
return new Gson().fromJson(reader, CustomObjectRepository.class);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package eu.crushedpixel.replaymod.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.assets.CustomObjectRepository;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||
import eu.crushedpixel.replaymod.holders.MarkerKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||
@@ -169,6 +170,10 @@ public class ReplayFileIO {
|
||||
write((Object) markers, file);
|
||||
}
|
||||
|
||||
public static void write(CustomObjectRepository repository, File file) throws IOException {
|
||||
write((Object) repository, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Files as entries to a ZIP File.
|
||||
* @param zipFile The ZIP File to add the files to.
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
package eu.crushedpixel.replaymod.utils;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class RoundUtils {
|
||||
|
||||
public static double round2Decimals(double val) { return Math.round(val*100.0) / 100.0; }
|
||||
public static double round2Decimals(double val) {
|
||||
return round(val, 2);
|
||||
}
|
||||
|
||||
public static double round(double value, int places) {
|
||||
if (places < 0) throw new IllegalArgumentException();
|
||||
|
||||
StringBuilder format = new StringBuilder("#.");
|
||||
for(int i=0; i<places; i++) {
|
||||
format.append("#");
|
||||
}
|
||||
|
||||
DecimalFormat df = new DecimalFormat(format.toString());
|
||||
return Double.valueOf(df.format(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -365,6 +365,7 @@ replaymod.gui.assets.defaultname=New Asset
|
||||
replaymod.gui.assets.emptylist=No Assets available
|
||||
replaymod.gui.assets.namehint=Asset Name
|
||||
replaymod.gui.assets.changefile=Change Asset File
|
||||
replaymod.gui.assets.noselection=No Asset selected
|
||||
|
||||
#Object Manager Gui
|
||||
replaymod.gui.objects.properties.anchor=Anchor Point
|
||||
|
||||
Reference in New Issue
Block a user