Register new Property to imply that the path segment following a keyframe has a user-set interpolator instead of the default
Modified Keyframe GUI to allow for custom Path interpolator
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
package com.replaymod.pathing.properties;
|
||||||
|
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import com.replaymod.replaystudio.pathing.property.AbstractProperty;
|
||||||
|
import com.replaymod.replaystudio.pathing.property.PropertyPart;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property indicating that the next path segment's interpolator is fixed
|
||||||
|
*/
|
||||||
|
public class ExplicitInterpolationProperty extends AbstractProperty<Object> {
|
||||||
|
public static final ExplicitInterpolationProperty PROPERTY = new ExplicitInterpolationProperty();
|
||||||
|
|
||||||
|
private ExplicitInterpolationProperty() {
|
||||||
|
super("interpolationFixed", "<internal>", null, new Object());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<PropertyPart<Object>> getParts() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyToGame(Object value, @NonNull Object replayHandler) {
|
||||||
|
// dummy property, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toJson(JsonWriter writer, Object value) throws IOException {
|
||||||
|
writer.nullValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void fromJson(JsonReader reader) throws IOException {
|
||||||
|
reader.nextNull();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import com.google.gson.stream.JsonWriter;
|
|||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
import com.replaymod.core.events.SettingsChangedEvent;
|
import com.replaymod.core.events.SettingsChangedEvent;
|
||||||
import com.replaymod.pathing.properties.CameraProperties;
|
import com.replaymod.pathing.properties.CameraProperties;
|
||||||
|
import com.replaymod.pathing.properties.ExplicitInterpolationProperty;
|
||||||
import com.replaymod.pathing.properties.SpectatorProperty;
|
import com.replaymod.pathing.properties.SpectatorProperty;
|
||||||
import com.replaymod.pathing.properties.TimestampProperty;
|
import com.replaymod.pathing.properties.TimestampProperty;
|
||||||
import com.replaymod.replay.events.ReplayCloseEvent;
|
import com.replaymod.replay.events.ReplayCloseEvent;
|
||||||
@@ -16,6 +17,7 @@ import com.replaymod.replaystudio.pathing.interpolation.Interpolator;
|
|||||||
import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator;
|
import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator;
|
||||||
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
||||||
import com.replaymod.replaystudio.pathing.path.Timeline;
|
import com.replaymod.replaystudio.pathing.path.Timeline;
|
||||||
|
import com.replaymod.replaystudio.pathing.property.Property;
|
||||||
import com.replaymod.simplepathing.gui.GuiPathing;
|
import com.replaymod.simplepathing.gui.GuiPathing;
|
||||||
import com.replaymod.simplepathing.preview.PathPreview;
|
import com.replaymod.simplepathing.preview.PathPreview;
|
||||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||||
@@ -24,7 +26,10 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
|||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
@Mod(modid = ReplayModSimplePathing.MOD_ID,
|
@Mod(modid = ReplayModSimplePathing.MOD_ID,
|
||||||
version = "@MOD_VERSION@",
|
version = "@MOD_VERSION@",
|
||||||
@@ -68,7 +73,7 @@ public class ReplayModSimplePathing implements PathingRegistry {
|
|||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onSettingsChanged(SettingsChangedEvent event) {
|
public void onSettingsChanged(SettingsChangedEvent event) {
|
||||||
if (event.getKey() == Setting.LINEAR_INTERPOLATION) {
|
if (event.getKey() == Setting.DEFAULT_INTERPOLATION) {
|
||||||
if (currentTimeline != null && guiPathing != null) {
|
if (currentTimeline != null && guiPathing != null) {
|
||||||
currentTimeline.applyChange(guiPathing.updateInterpolators());
|
currentTimeline.applyChange(guiPathing.updateInterpolators());
|
||||||
}
|
}
|
||||||
@@ -105,6 +110,7 @@ public class ReplayModSimplePathing implements PathingRegistry {
|
|||||||
timeline.registerProperty(CameraProperties.POSITION);
|
timeline.registerProperty(CameraProperties.POSITION);
|
||||||
timeline.registerProperty(CameraProperties.ROTATION);
|
timeline.registerProperty(CameraProperties.ROTATION);
|
||||||
timeline.registerProperty(SpectatorProperty.PROPERTY);
|
timeline.registerProperty(SpectatorProperty.PROPERTY);
|
||||||
|
timeline.registerProperty(ExplicitInterpolationProperty.PROPERTY);
|
||||||
|
|
||||||
return timeline;
|
return timeline;
|
||||||
}
|
}
|
||||||
@@ -134,6 +140,35 @@ public class ReplayModSimplePathing implements PathingRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones an interpolator by de- and reserializing it.
|
||||||
|
* @param interpolator The interpolator to clone
|
||||||
|
* @return The cloned interpolator
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public Interpolator cloneInterpolator(Interpolator interpolator) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
PrintWriter printWriter = new PrintWriter(baos);
|
||||||
|
|
||||||
|
JsonWriter jsonWriter = new JsonWriter(printWriter);
|
||||||
|
jsonWriter.beginArray();
|
||||||
|
serializeInterpolator(jsonWriter, interpolator);
|
||||||
|
jsonWriter.endArray();
|
||||||
|
jsonWriter.flush();
|
||||||
|
|
||||||
|
String json = baos.toString();
|
||||||
|
|
||||||
|
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
||||||
|
jsonReader.beginArray();
|
||||||
|
Interpolator cloned = deserializeInterpolator(jsonReader);
|
||||||
|
|
||||||
|
for (Property p : interpolator.getKeyframeProperties()) {
|
||||||
|
cloned.registerProperty(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
public ReplayMod getCore() {
|
public ReplayMod getCore() {
|
||||||
return core;
|
return core;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,23 @@ package com.replaymod.simplepathing;
|
|||||||
|
|
||||||
import com.replaymod.core.SettingsRegistry;
|
import com.replaymod.core.SettingsRegistry;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
|
public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
|
||||||
public static final Setting<Boolean> PATH_PREVIEW = make("pathpreview", "pathpreview", true);
|
public static final Setting<Boolean> PATH_PREVIEW = make("pathpreview", "pathpreview", true);
|
||||||
public static final Setting<Boolean> LINEAR_INTERPOLATION = make("linear_interpolation", "linearinterpolation", false);
|
public static final SettingsRegistry.MultipleChoiceSettingKeys<String> DEFAULT_INTERPOLATION =
|
||||||
|
new SettingsRegistry.MultipleChoiceSettingKeys<>(
|
||||||
|
"simplepathing", "interpolator", "replaymod.gui.settings.interpolator",
|
||||||
|
"replaymod.gui.editkeyframe.interpolator.cubic.name");
|
||||||
|
|
||||||
|
static {
|
||||||
|
DEFAULT_INTERPOLATION.setChoices(new ArrayList<String>() {
|
||||||
|
{
|
||||||
|
add("replaymod.gui.editkeyframe.interpolator.cubic.name");
|
||||||
|
add("replaymod.gui.editkeyframe.interpolator.linear.name");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
|
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
|
||||||
return new Setting<>(key, displayName, defaultValue);
|
return new Setting<>(key, displayName, defaultValue);
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
package com.replaymod.simplepathing.gui;
|
package com.replaymod.simplepathing.gui;
|
||||||
|
|
||||||
import com.replaymod.pathing.properties.CameraProperties;
|
import com.replaymod.pathing.properties.CameraProperties;
|
||||||
|
import com.replaymod.pathing.properties.ExplicitInterpolationProperty;
|
||||||
import com.replaymod.pathing.properties.TimestampProperty;
|
import com.replaymod.pathing.properties.TimestampProperty;
|
||||||
import com.replaymod.replay.ReplayModReplay;
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
import com.replaymod.replaystudio.pathing.change.Change;
|
import com.replaymod.replaystudio.pathing.change.Change;
|
||||||
import com.replaymod.replaystudio.pathing.change.CombinedChange;
|
import com.replaymod.replaystudio.pathing.change.CombinedChange;
|
||||||
|
import com.replaymod.replaystudio.pathing.change.SetInterpolator;
|
||||||
import com.replaymod.replaystudio.pathing.change.UpdateKeyframeProperties;
|
import com.replaymod.replaystudio.pathing.change.UpdateKeyframeProperties;
|
||||||
|
import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator;
|
||||||
|
import com.replaymod.replaystudio.pathing.interpolation.Interpolator;
|
||||||
|
import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator;
|
||||||
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
||||||
import com.replaymod.replaystudio.pathing.path.Path;
|
import com.replaymod.replaystudio.pathing.path.Path;
|
||||||
|
import com.replaymod.replaystudio.pathing.path.PathSegment;
|
||||||
|
import com.replaymod.simplepathing.Setting;
|
||||||
|
import com.replaymod.simplepathing.gui.GuiEditKeyframe.Position.InterpolationPanel.InterpolatorType;
|
||||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
import de.johni0702.minecraft.gui.element.GuiButton;
|
import de.johni0702.minecraft.gui.element.*;
|
||||||
import de.johni0702.minecraft.gui.element.GuiLabel;
|
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
|
||||||
import de.johni0702.minecraft.gui.element.GuiNumberField;
|
|
||||||
import de.johni0702.minecraft.gui.element.IGuiLabel;
|
|
||||||
import de.johni0702.minecraft.gui.function.Typeable;
|
import de.johni0702.minecraft.gui.function.Typeable;
|
||||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
@@ -20,10 +26,19 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
|||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
import org.apache.commons.lang3.tuple.Triple;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
import org.lwjgl.util.ReadablePoint;
|
import org.lwjgl.util.ReadablePoint;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.replaymod.simplepathing.gui.GuiEditKeyframe.Position.InterpolationPanel.InterpolatorSettingsPanel.CubicInterpolatorSettingsPanel;
|
||||||
|
import static com.replaymod.simplepathing.gui.GuiEditKeyframe.Position.InterpolationPanel.InterpolatorSettingsPanel.LinearInterpolatorSettingsPanel;
|
||||||
import static de.johni0702.minecraft.gui.utils.Utils.link;
|
import static de.johni0702.minecraft.gui.utils.Utils.link;
|
||||||
|
|
||||||
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> implements Typeable {
|
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> implements Typeable {
|
||||||
@@ -31,6 +46,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true);
|
return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
protected final GuiPathing guiPathing;
|
||||||
|
|
||||||
protected final Keyframe keyframe;
|
protected final Keyframe keyframe;
|
||||||
protected final Path path;
|
protected final Path path;
|
||||||
|
|
||||||
@@ -68,6 +87,7 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
|
|
||||||
public GuiEditKeyframe(GuiPathing gui, Path path, Keyframe keyframe, String type) {
|
public GuiEditKeyframe(GuiPathing gui, Path path, Keyframe keyframe, String type) {
|
||||||
super(ReplayModReplay.instance.getReplayHandler().getOverlay());
|
super(ReplayModReplay.instance.getReplayHandler().getOverlay());
|
||||||
|
this.guiPathing = gui;
|
||||||
this.keyframe = keyframe;
|
this.keyframe = keyframe;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
|
||||||
@@ -187,8 +207,11 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
public final GuiNumberField pitchField = newGuiNumberField().setSize(60, 20).setPrecision(5);
|
public final GuiNumberField pitchField = newGuiNumberField().setSize(60, 20).setPrecision(5);
|
||||||
public final GuiNumberField rollField = newGuiNumberField().setSize(60, 20).setPrecision(5);
|
public final GuiNumberField rollField = newGuiNumberField().setSize(60, 20).setPrecision(5);
|
||||||
|
|
||||||
|
public final InterpolationPanel interpolationPanel = new InterpolationPanel(guiPathing);
|
||||||
|
|
||||||
{
|
{
|
||||||
inputs.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(4).setSpacingX(3).setSpacingY(5))
|
GuiPanel positionInputs = new GuiPanel()
|
||||||
|
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(4).setSpacingX(3).setSpacingY(5))
|
||||||
.addElements(new GridLayout.Data(1, 0.5),
|
.addElements(new GridLayout.Data(1, 0.5),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.xpos"), xField,
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.xpos"), xField,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.camyaw"), yawField,
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.camyaw"), yawField,
|
||||||
@@ -196,6 +219,9 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.campitch"), pitchField,
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.campitch"), pitchField,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.zpos"), zField,
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.zpos"), zField,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.camroll"), rollField);
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.camroll"), rollField);
|
||||||
|
|
||||||
|
inputs.setLayout(new VerticalLayout().setSpacing(10)).addElements(new VerticalLayout.Data(0.5, false),
|
||||||
|
positionInputs, interpolationPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Position(GuiPathing gui, Path path, Keyframe keyframe) {
|
public Position(GuiPathing gui, Path path, Keyframe keyframe) {
|
||||||
@@ -219,17 +245,193 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Change save() {
|
protected Change save() {
|
||||||
Change change = UpdateKeyframeProperties.create(path, keyframe)
|
Change setInterpolatorChange = null;
|
||||||
|
|
||||||
|
UpdateKeyframeProperties.Builder builder = UpdateKeyframeProperties.create(path, keyframe)
|
||||||
.setValue(CameraProperties.POSITION, Triple.of(xField.getDouble(), yField.getDouble(), zField.getDouble()))
|
.setValue(CameraProperties.POSITION, Triple.of(xField.getDouble(), yField.getDouble(), zField.getDouble()))
|
||||||
.setValue(CameraProperties.ROTATION, Triple.of(yawField.getFloat(), pitchField.getFloat(), rollField.getFloat()))
|
.setValue(CameraProperties.ROTATION, Triple.of(yawField.getFloat(), pitchField.getFloat(), rollField.getFloat()))
|
||||||
.done();
|
.removeProperty(ExplicitInterpolationProperty.PROPERTY);
|
||||||
change.apply(path.getTimeline());
|
|
||||||
return change;
|
// if the interpolator is not the default, set the ExplicitInterpolationProperty flag
|
||||||
|
if (interpolationPanel.getInterpolatorType() != InterpolatorType.DEFAULT) {
|
||||||
|
PathSegment toModify = null;
|
||||||
|
for (PathSegment segment : path.getSegments()) {
|
||||||
|
if (segment.getStartKeyframe() == keyframe) {
|
||||||
|
toModify = segment;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toModify != null) {
|
||||||
|
builder.setValue(ExplicitInterpolationProperty.PROPERTY, new Object());
|
||||||
|
|
||||||
|
Interpolator interpolator = interpolationPanel.getInterpolatorSettingsPanel().createInterpolator();
|
||||||
|
interpolator.registerProperty(CameraProperties.POSITION);
|
||||||
|
interpolator.registerProperty(CameraProperties.ROTATION);
|
||||||
|
|
||||||
|
setInterpolatorChange = SetInterpolator.create(toModify, interpolator);
|
||||||
|
} else {
|
||||||
|
logger.warn("The Path segment to modify was not found. Setting interpolator to default.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Change keyframePropertiesChange = builder.done();
|
||||||
|
keyframePropertiesChange.apply(path.getTimeline());
|
||||||
|
|
||||||
|
if (setInterpolatorChange == null) {
|
||||||
|
return keyframePropertiesChange;
|
||||||
|
} else {
|
||||||
|
setInterpolatorChange.apply(path.getTimeline());
|
||||||
|
guiPathing.updateInterpolators();
|
||||||
|
path.updateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CombinedChange.createFromApplied(keyframePropertiesChange, setInterpolatorChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Position getThis() {
|
protected Position getThis() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class InterpolationPanel extends de.johni0702.minecraft.gui.container.AbstractGuiContainer<InterpolationPanel> {
|
||||||
|
|
||||||
|
private final GuiPathing guiPathing;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private InterpolatorSettingsPanel interpolatorSettingsPanel;
|
||||||
|
|
||||||
|
private GuiDropdownMenu<InterpolatorType> dropdown;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum InterpolatorType {
|
||||||
|
DEFAULT("default", null),
|
||||||
|
CUBIC("cubic", CubicSplineInterpolator.class),
|
||||||
|
LINEAR("linear", LinearInterpolator.class);
|
||||||
|
|
||||||
|
private String localizationKey;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private Class<? extends Interpolator> interpolatorClass;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return I18n.format(String.format("replaymod.gui.editkeyframe.interpolator.%1$s.name", localizationKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getI18nDescription() {
|
||||||
|
return String.format("replaymod.gui.editkeyframe.interpolator.%1$s.desc", localizationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InterpolatorType fromString(String string) {
|
||||||
|
for (InterpolatorType t : values()) {
|
||||||
|
if (t.toString().equals(string)) return t;
|
||||||
|
}
|
||||||
|
return CUBIC; //the default
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public InterpolationPanel(GuiPathing guiPathing) {
|
||||||
|
this.guiPathing = guiPathing;
|
||||||
|
|
||||||
|
setLayout(new VerticalLayout());
|
||||||
|
|
||||||
|
dropdown = new GuiDropdownMenu<InterpolatorType>().setValues(InterpolatorType.values()).setHeight(20);
|
||||||
|
dropdown.onSelection((index) -> setSettingsPanel(dropdown.getSelectedValue()));
|
||||||
|
|
||||||
|
// set hover tooltips
|
||||||
|
for (Map.Entry<InterpolatorType, IGuiClickable> e : dropdown.getDropdownEntries().entrySet()) {
|
||||||
|
e.getValue().setTooltip(new GuiTooltip().setI18nText(e.getKey().getI18nDescription()));
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiPanel dropdownPanel = new GuiPanel()
|
||||||
|
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(3).setSpacingY(5))
|
||||||
|
.addElements(new GridLayout.Data(1, 0.5),
|
||||||
|
new GuiLabel().setI18nText("replaymod.gui.editkeyframe.interpolator"), dropdown);
|
||||||
|
|
||||||
|
|
||||||
|
addElements(new VerticalLayout.Data(0.5, false), dropdownPanel);
|
||||||
|
|
||||||
|
dropdown.onSelection(0); // trigger the callback once to display settings panel
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSettingsPanel(InterpolatorType type) {
|
||||||
|
removeElement(this.interpolatorSettingsPanel);
|
||||||
|
|
||||||
|
InterpolatorSettingsPanel settingsPanel = null;
|
||||||
|
switch (getInterpolatorTypeNoDefault(type)) {
|
||||||
|
case CUBIC:
|
||||||
|
settingsPanel = new CubicInterpolatorSettingsPanel();
|
||||||
|
break;
|
||||||
|
case LINEAR:
|
||||||
|
settingsPanel = new LinearInterpolatorSettingsPanel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
addElements(new GridLayout.Data(0.5, 0.5), settingsPanel);
|
||||||
|
|
||||||
|
this.interpolatorSettingsPanel = settingsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InterpolatorType getInterpolatorTypeNoDefault(InterpolatorType interpolatorType) {
|
||||||
|
if (interpolatorType == InterpolatorType.DEFAULT || interpolatorType == null) {
|
||||||
|
InterpolatorType defaultType = InterpolatorType.fromString(
|
||||||
|
guiPathing.getMod().getCore().getSettingsRegistry().get(Setting.DEFAULT_INTERPOLATION));
|
||||||
|
return defaultType;
|
||||||
|
}
|
||||||
|
return interpolatorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InterpolatorType getInterpolatorType() {
|
||||||
|
return dropdown.getSelectedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InterpolationPanel getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class InterpolatorSettingsPanel<I extends Interpolator, T extends InterpolatorSettingsPanel> extends de.johni0702.minecraft.gui.container.GuiPanel {
|
||||||
|
|
||||||
|
public abstract void loadSettings(I interpolator);
|
||||||
|
|
||||||
|
public abstract I createInterpolator();
|
||||||
|
|
||||||
|
public static class CubicInterpolatorSettingsPanel extends InterpolatorSettingsPanel<CubicSplineInterpolator, CubicInterpolatorSettingsPanel> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadSettings(CubicSplineInterpolator interpolator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CubicSplineInterpolator createInterpolator() {
|
||||||
|
return new CubicSplineInterpolator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InterpolatorSettingsPanel<CubicSplineInterpolator, CubicInterpolatorSettingsPanel> getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LinearInterpolatorSettingsPanel extends InterpolatorSettingsPanel<LinearInterpolator, LinearInterpolatorSettingsPanel> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadSettings(LinearInterpolator interpolator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinearInterpolator createInterpolator() {
|
||||||
|
return new LinearInterpolator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InterpolatorSettingsPanel<LinearInterpolator, LinearInterpolatorSettingsPanel> getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import com.google.common.util.concurrent.Futures;
|
|||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.pathing.gui.GuiKeyframeRepository;
|
import com.replaymod.pathing.gui.GuiKeyframeRepository;
|
||||||
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
|
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
|
||||||
import com.replaymod.pathing.properties.CameraProperties;
|
import com.replaymod.pathing.properties.CameraProperties;
|
||||||
|
import com.replaymod.pathing.properties.ExplicitInterpolationProperty;
|
||||||
import com.replaymod.pathing.properties.SpectatorProperty;
|
import com.replaymod.pathing.properties.SpectatorProperty;
|
||||||
import com.replaymod.pathing.properties.TimestampProperty;
|
import com.replaymod.pathing.properties.TimestampProperty;
|
||||||
import com.replaymod.render.gui.GuiRenderSettings;
|
import com.replaymod.render.gui.GuiRenderSettings;
|
||||||
@@ -28,6 +30,7 @@ import com.replaymod.replaystudio.util.EntityPositionTracker;
|
|||||||
import com.replaymod.replaystudio.util.Location;
|
import com.replaymod.replaystudio.util.Location;
|
||||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||||
import com.replaymod.simplepathing.Setting;
|
import com.replaymod.simplepathing.Setting;
|
||||||
|
import com.replaymod.simplepathing.gui.GuiEditKeyframe.Position.InterpolationPanel.InterpolatorType;
|
||||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||||
import de.johni0702.minecraft.gui.RenderInfo;
|
import de.johni0702.minecraft.gui.RenderInfo;
|
||||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||||
@@ -57,10 +60,7 @@ import org.lwjgl.util.WritablePoint;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static com.replaymod.core.utils.Utils.error;
|
import static com.replaymod.core.utils.Utils.error;
|
||||||
@@ -637,11 +637,48 @@ public class GuiPathing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Change updateInterpolators() {
|
public Change updateInterpolators() {
|
||||||
boolean linearInterpolation = mod.getCore().getSettingsRegistry().get(Setting.LINEAR_INTERPOLATION);
|
InterpolatorType type = InterpolatorType.fromString(mod.getCore().getSettingsRegistry().get(Setting.DEFAULT_INTERPOLATION));
|
||||||
|
|
||||||
|
Interpolator defaultInterpolator = null;
|
||||||
|
|
||||||
|
// Iterate over all existing segments until the first interpolator
|
||||||
|
// of the same type without the fixed flag is found and clone it.
|
||||||
|
// This way, the default interpolator's settings can be saved on a per-camera-path basis.
|
||||||
|
// This is useful for interpolators that have customizable properties.
|
||||||
|
Collection<PathSegment> pathSegments = mod.getCurrentTimeline().getPaths().get(POSITION_PATH).getSegments();
|
||||||
|
|
||||||
|
for (PathSegment segment : pathSegments) {
|
||||||
|
if (segment.getStartKeyframe().getValue(ExplicitInterpolationProperty.PROPERTY).isPresent()) continue;
|
||||||
|
if (segment.getInterpolator() == null) continue;
|
||||||
|
if (type == InterpolatorType.DEFAULT || type.getInterpolatorClass().equals(segment.getInterpolator().getClass())) {
|
||||||
|
try {
|
||||||
|
defaultInterpolator = mod.cloneInterpolator(segment.getInterpolator());
|
||||||
|
break;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Utils.error(LOGGER, replayHandler.getOverlay(),
|
||||||
|
CrashReport.makeCrashReport(e, "Cloning fixed interpolator"), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no segment with the default interpolator was found,
|
||||||
|
// create a new one with default settings
|
||||||
|
if (defaultInterpolator == null) {
|
||||||
|
switch (type) {
|
||||||
|
case LINEAR:
|
||||||
|
defaultInterpolator = new LinearInterpolator();
|
||||||
|
break;
|
||||||
|
case CUBIC:
|
||||||
|
default:
|
||||||
|
defaultInterpolator = new CubicSplineInterpolator();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Change> changes = new ArrayList<>();
|
List<Change> changes = new ArrayList<>();
|
||||||
Interpolator interpolator = null;
|
Interpolator interpolator = null;
|
||||||
boolean isSpectatorInterpolator = false;
|
boolean isSpectatorInterpolator = false;
|
||||||
for (PathSegment segment : mod.getCurrentTimeline().getPaths().get(POSITION_PATH).getSegments()) {
|
for (PathSegment segment : pathSegments) {
|
||||||
if (segment.getStartKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()
|
if (segment.getStartKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()
|
||||||
&& segment.getEndKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()) {
|
&& segment.getEndKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()) {
|
||||||
// Spectator segment
|
// Spectator segment
|
||||||
@@ -653,11 +690,23 @@ public class GuiPathing {
|
|||||||
changes.add(SetInterpolator.create(segment, interpolator));
|
changes.add(SetInterpolator.create(segment, interpolator));
|
||||||
} else {
|
} else {
|
||||||
// Normal segment
|
// Normal segment
|
||||||
if (isSpectatorInterpolator || interpolator == null) {
|
boolean explicit = segment.getStartKeyframe().getValue(ExplicitInterpolationProperty.PROPERTY).isPresent();
|
||||||
|
if (isSpectatorInterpolator || interpolator == null || explicit) {
|
||||||
isSpectatorInterpolator = false;
|
isSpectatorInterpolator = false;
|
||||||
interpolator = linearInterpolation ? new LinearInterpolator() : new CubicSplineInterpolator();
|
|
||||||
interpolator.registerProperty(CameraProperties.POSITION);
|
if (segment.getInterpolator() == null || !explicit) {
|
||||||
interpolator.registerProperty(CameraProperties.ROTATION);
|
interpolator = defaultInterpolator;
|
||||||
|
interpolator.registerProperty(CameraProperties.POSITION);
|
||||||
|
interpolator.registerProperty(CameraProperties.ROTATION);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
interpolator = mod.cloneInterpolator(segment.getInterpolator());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Utils.error(LOGGER, replayHandler.getOverlay(),
|
||||||
|
CrashReport.makeCrashReport(e, "Cloning fixed interpolator"), null);
|
||||||
|
interpolator = defaultInterpolator;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
changes.add(SetInterpolator.create(segment, interpolator));
|
changes.add(SetInterpolator.create(segment, interpolator));
|
||||||
}
|
}
|
||||||
@@ -697,10 +746,18 @@ public class GuiPathing {
|
|||||||
|
|
||||||
public Change moveKeyframe(Path path, Keyframe keyframe, long newTime) {
|
public Change moveKeyframe(Path path, Keyframe keyframe, long newTime) {
|
||||||
Timeline timeline = mod.getCurrentTimeline();
|
Timeline timeline = mod.getCurrentTimeline();
|
||||||
// Interpolator might be required later (only if path is the time path)
|
// InterpolatorType might be required later (only if path is the time path)
|
||||||
Optional<Interpolator> interpolator =
|
Optional<Interpolator> firstInterpolator =
|
||||||
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
|
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
|
||||||
|
|
||||||
|
// The interpolator before the segment
|
||||||
|
Optional<Interpolator> interpolatorBefore =
|
||||||
|
path.getSegments().stream().filter(s -> s.getEndKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator);
|
||||||
|
|
||||||
|
// The interpolator that follows the segment
|
||||||
|
Optional<Interpolator> interpolatorAfter =
|
||||||
|
path.getSegments().stream().filter(s -> s.getStartKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator);
|
||||||
|
|
||||||
// First remove the old keyframe
|
// First remove the old keyframe
|
||||||
Change removeChange = RemoveKeyframe.create(path, keyframe);
|
Change removeChange = RemoveKeyframe.create(path, keyframe);
|
||||||
removeChange.apply(timeline);
|
removeChange.apply(timeline);
|
||||||
@@ -708,7 +765,7 @@ public class GuiPathing {
|
|||||||
// and add a new one at the correct time
|
// and add a new one at the correct time
|
||||||
Change addChange = AddKeyframe.create(path, newTime);
|
Change addChange = AddKeyframe.create(path, newTime);
|
||||||
addChange.apply(timeline);
|
addChange.apply(timeline);
|
||||||
path.getKeyframe(newTime);
|
Keyframe newKeyframe = path.getKeyframe(newTime);
|
||||||
|
|
||||||
// Then copy over all properties
|
// Then copy over all properties
|
||||||
UpdateKeyframeProperties.Builder builder = UpdateKeyframeProperties.create(path, path.getKeyframe(newTime));
|
UpdateKeyframeProperties.Builder builder = UpdateKeyframeProperties.create(path, path.getKeyframe(newTime));
|
||||||
@@ -718,25 +775,51 @@ public class GuiPathing {
|
|||||||
Change propertyChange = builder.done();
|
Change propertyChange = builder.done();
|
||||||
propertyChange.apply(timeline);
|
propertyChange.apply(timeline);
|
||||||
|
|
||||||
// Finally set the interpolators
|
// Set the interpolator of the segment before the keyframe to what it was
|
||||||
Change interpolatorChange;
|
Change interpolatorBeforeChange;
|
||||||
|
|
||||||
|
Optional<PathSegment> segmentBefore = path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKeyframe).findFirst();
|
||||||
|
if (segmentBefore.isPresent() && interpolatorBefore.isPresent()) {
|
||||||
|
interpolatorBeforeChange = SetInterpolator.create(segmentBefore.get(), interpolatorBefore.get());
|
||||||
|
} else {
|
||||||
|
interpolatorBeforeChange = CombinedChange.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
interpolatorBeforeChange.apply(timeline);
|
||||||
|
|
||||||
|
// Set the interpolator of the segment after the keyframe to what it was
|
||||||
|
Change interpolatorAfterChange;
|
||||||
|
|
||||||
|
Optional<PathSegment> segmentAfter = path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKeyframe).findFirst();
|
||||||
|
if (segmentAfter.isPresent() && interpolatorAfter.isPresent()) {
|
||||||
|
interpolatorAfterChange = SetInterpolator.create(segmentAfter.get(), interpolatorAfter.get());
|
||||||
|
} else {
|
||||||
|
interpolatorAfterChange = CombinedChange.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
interpolatorAfterChange.apply(timeline);
|
||||||
|
|
||||||
|
// Finally update the interpolators
|
||||||
|
Change interpolatorUpdateChange;
|
||||||
if (path.getTimeline().getPaths().indexOf(path) == GuiPathing.POSITION_PATH) {
|
if (path.getTimeline().getPaths().indexOf(path) == GuiPathing.POSITION_PATH) {
|
||||||
// Position / Spectator keyframes need special handling
|
// Position / Spectator keyframes need special handling
|
||||||
interpolatorChange = updateInterpolators();
|
interpolatorUpdateChange = updateInterpolators();
|
||||||
} else {
|
} else {
|
||||||
// Time keyframes only need updating when only one segment of them exists
|
// Time keyframes only need updating when only one segment of them exists
|
||||||
if (path.getSegments().size() == 1) {
|
if (path.getSegments().size() == 1) {
|
||||||
interpolatorChange = SetInterpolator.create(path.getSegments().iterator().next(), interpolator.get());
|
interpolatorUpdateChange = SetInterpolator.create(path.getSegments().iterator().next(), firstInterpolator.get());
|
||||||
} else {
|
} else {
|
||||||
interpolatorChange = CombinedChange.create(); // Noop change
|
interpolatorUpdateChange = CombinedChange.create(); // Noop change
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interpolatorChange.apply(timeline);
|
|
||||||
|
interpolatorUpdateChange.apply(timeline);
|
||||||
// and update spectator positions
|
// and update spectator positions
|
||||||
Change spectatorChange = updateSpectatorPositions();
|
Change spectatorChange = updateSpectatorPositions();
|
||||||
spectatorChange.apply(timeline);
|
spectatorChange.apply(timeline);
|
||||||
|
|
||||||
return CombinedChange.createFromApplied(removeChange, addChange, propertyChange, interpolatorChange, spectatorChange);
|
return CombinedChange.createFromApplied(removeChange, addChange, propertyChange,
|
||||||
|
interpolatorBeforeChange, interpolatorAfterChange, interpolatorUpdateChange, spectatorChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method because generics cannot be defined on blocks
|
// Helper method because generics cannot be defined on blocks
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ replaymod.gui.settings.keyframecleancallback=Clear Confirmation
|
|||||||
replaymod.gui.settings.renderinvisible=Render invisible Entities
|
replaymod.gui.settings.renderinvisible=Render invisible Entities
|
||||||
replaymod.gui.settings.camera=Camera
|
replaymod.gui.settings.camera=Camera
|
||||||
replaymod.gui.settings.showchat=Show Chat
|
replaymod.gui.settings.showchat=Show Chat
|
||||||
|
replaymod.gui.settings.interpolator=Default Interpolator
|
||||||
|
|
||||||
replaymod.gui.settings.warning.linea=WARNING: Recording settings will be
|
replaymod.gui.settings.warning.linea=WARNING: Recording settings will be
|
||||||
replaymod.gui.settings.warning.lineb=applied the next time you join a world.
|
replaymod.gui.settings.warning.lineb=applied the next time you join a world.
|
||||||
@@ -372,6 +373,14 @@ replaymod.gui.editkeyframe.spec.method.shoulder.pitch=Pitch Offset
|
|||||||
replaymod.gui.editkeyframe.spec.method.shoulder.yaw=Rotation Angle
|
replaymod.gui.editkeyframe.spec.method.shoulder.yaw=Rotation Angle
|
||||||
replaymod.gui.editkeyframe.spec.method.shoulder.smoothness=Path Smoothness
|
replaymod.gui.editkeyframe.spec.method.shoulder.smoothness=Path Smoothness
|
||||||
|
|
||||||
|
replaymod.gui.editkeyframe.interpolator=Interpolator
|
||||||
|
replaymod.gui.editkeyframe.interpolator.default.name=Default Interpolator
|
||||||
|
replaymod.gui.editkeyframe.interpolator.default.desc=Uses the default interpolator defined in the Replay Mod Settings.
|
||||||
|
replaymod.gui.editkeyframe.interpolator.cubic.name=Cubic Spline Interpolator
|
||||||
|
replaymod.gui.editkeyframe.interpolator.cubic.desc=Calculates a cubic equation matrix for all points for a smooth camera path.
|
||||||
|
replaymod.gui.editkeyframe.interpolator.linear.name=Linear Interpolator
|
||||||
|
replaymod.gui.editkeyframe.interpolator.linear.desc=Draws straight lines between the keyframes.
|
||||||
|
|
||||||
#Render Settings GUI
|
#Render Settings GUI
|
||||||
replaymod.gui.rendersettings.title=Rendering Options
|
replaymod.gui.rendersettings.title=Rendering Options
|
||||||
replaymod.gui.rendersettings.renderer=Rendering Method
|
replaymod.gui.rendersettings.renderer=Rendering Method
|
||||||
|
|||||||
Reference in New Issue
Block a user