diff --git a/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java b/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java index b41ad1ee..3c942bb5 100644 --- a/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java +++ b/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java @@ -12,6 +12,7 @@ import com.replaymod.replay.ReplayHandler; import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Timeline; +import com.replaymod.pathing.properties.FovProperty; import de.johni0702.minecraft.gui.utils.EventRegistrations; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderTickCounter; @@ -114,6 +115,7 @@ public abstract class AbstractTimelinePlayer extends EventRegistrations { if (wasAsyncMode) { replayHandler.getReplaySender().setAsyncMode(true); } + FovProperty.currentOverride = null; unregister(); return; } diff --git a/src/main/java/com/replaymod/pathing/properties/FovProperty.java b/src/main/java/com/replaymod/pathing/properties/FovProperty.java new file mode 100644 index 00000000..79c00591 --- /dev/null +++ b/src/main/java/com/replaymod/pathing/properties/FovProperty.java @@ -0,0 +1,48 @@ +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 com.replaymod.replaystudio.pathing.property.PropertyParts; +import de.johni0702.minecraft.gui.utils.NonNull; +import org.apache.commons.lang3.tuple.Triple; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +public class FovProperty extends AbstractProperty> { + public static final float DEFAULT_FOV = 70.0f; + + public static volatile Float currentOverride = null; + + public static final FovProperty FOV = new FovProperty("fov", DEFAULT_FOV); + + public final PropertyPart> VALUE = + new PropertyParts.ForFloatTriple(this, true, PropertyParts.TripleElement.LEFT); + + private FovProperty(String id, float defaultValue) { + super(id, "replaymod.gui.editkeyframe." + id, null, Triple.of(defaultValue, 0f, 0f)); + } + + @Override + public Collection>> getParts() { + return Collections.singletonList(VALUE); + } + + @Override + public void applyToGame(Triple value, @NonNull Object replayHandler) { + currentOverride = value.getLeft(); + } + + @Override + public void toJson(JsonWriter writer, Triple value) throws IOException { + writer.value(value.getLeft()); + } + + @Override + public Triple fromJson(JsonReader reader) throws IOException { + return Triple.of((float) reader.nextDouble(), 0f, 0f); + } +} \ No newline at end of file diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_DynamicFov.java b/src/main/java/com/replaymod/render/mixin/Mixin_DynamicFov.java new file mode 100644 index 00000000..0570391a --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_DynamicFov.java @@ -0,0 +1,38 @@ +package com.replaymod.render.mixin; + +import com.replaymod.pathing.properties.FovProperty; +import net.minecraft.client.render.GameRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(GameRenderer.class) +public abstract class Mixin_DynamicFov { + private static final String METHOD = "getBasicProjectionMatrix"; + //#if MC>=11903 + //#if MC>=12005 + //$$ private static final String TARGET = "Lorg/joml/Matrix4f;perspective(FFFF)Lorg/joml/Matrix4f;"; + //#else + //$$ private static final String TARGET = "Lorg/joml/Matrix4f;setPerspective(FFFF)Lorg/joml/Matrix4f;"; + //#endif + //$$ private static final boolean TARGET_REMAP = false; + //#else + private static final String TARGET = "Lnet/minecraft/util/math/Matrix4f;viewboxMatrix(DFFF)Lnet/minecraft/util/math/Matrix4f;"; + private static final boolean TARGET_REMAP = true; + //#endif + + @ModifyArg(method = METHOD, at = @At(value = "INVOKE", target = TARGET, remap = TARGET_REMAP), index = 0) + //#if MC>=11903 + //$$ private float replayModRender_dynamicFov(float fovY) { + //$$ Float override = FovProperty.currentOverride; + //$$ if (override == null) return fovY; + //$$ return (float) Math.toRadians(Math.max(1f, Math.min(179f, override))); + //$$ } + //#else + private double replayModRender_dynamicFov(double fovY) { + Float override = FovProperty.currentOverride; + if (override == null) return fovY; + return Math.max(1.0, Math.min(179.0, override)); + } + //#endif +} \ No newline at end of file diff --git a/src/main/java/com/replaymod/simplepathing/SPTimeline.java b/src/main/java/com/replaymod/simplepathing/SPTimeline.java index 26885e3a..61de86f8 100644 --- a/src/main/java/com/replaymod/simplepathing/SPTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/SPTimeline.java @@ -10,6 +10,7 @@ import com.replaymod.pathing.properties.CameraProperties; import com.replaymod.pathing.properties.SpectatorProperty; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.pathing.properties.LensProperties; +import com.replaymod.pathing.properties.FovProperty; import com.replaymod.replaystudio.pathing.PathingRegistry; import com.replaymod.replaystudio.pathing.change.AddKeyframe; import com.replaymod.replaystudio.pathing.change.Change; @@ -175,6 +176,7 @@ public class SPTimeline implements PathingRegistry { builder.setValue(CameraProperties.ROTATION, Triple.of(yaw, pitch, roll)); builder.setValue(LensProperties.FOCAL_DISTANCE, Triple.of(LensProperties.DEFAULT_FOCAL_DISTANCE, 0f, 0f)); builder.setValue(LensProperties.APERTURE_RADIUS, Triple.of(LensProperties.DEFAULT_APERTURE_RADIUS, 0f, 0f)); + builder.setValue(FovProperty.FOV, Triple.of(FovProperty.DEFAULT_FOV, 0f, 0f)); if (spectated != -1) { builder.setValue(SpectatorProperty.PROPERTY, spectated); } @@ -322,6 +324,16 @@ public class SPTimeline implements PathingRegistry { return change; } + public Change updateFov(long time, float fov) { + Keyframe keyframe = positionPath.getKeyframe(time); + Preconditions.checkState(keyframe != null, "Keyframe does not exist"); + Change change = UpdateKeyframeProperties.create(positionPath, keyframe) + .setValue(FovProperty.FOV, Triple.of(fov, 0f, 0f)) + .done(); + change.apply(timeline); + return change; + } + public Change setInterpolatorToDefault(long time) { LOGGER.debug("Setting interpolator of position keyframe at {} to the default", time); @@ -468,6 +480,7 @@ public class SPTimeline implements PathingRegistry { interpolator.registerProperty(SpectatorProperty.PROPERTY); interpolator.registerProperty(LensProperties.FOCAL_DISTANCE); interpolator.registerProperty(LensProperties.APERTURE_RADIUS); + interpolator.registerProperty(FovProperty.FOV); } // Now that we have an interpolator, set it for the current segment updates.put(segment, interpolator); @@ -588,6 +601,7 @@ public class SPTimeline implements PathingRegistry { interpolator.registerProperty(CameraProperties.ROTATION); interpolator.registerProperty(LensProperties.FOCAL_DISTANCE); interpolator.registerProperty(LensProperties.APERTURE_RADIUS); + interpolator.registerProperty(FovProperty.FOV); return interpolator; } @@ -612,6 +626,7 @@ public class SPTimeline implements PathingRegistry { timeline.registerProperty(SpectatorProperty.PROPERTY); timeline.registerProperty(LensProperties.FOCAL_DISTANCE); timeline.registerProperty(LensProperties.APERTURE_RADIUS); + timeline.registerProperty(FovProperty.FOV); timeline.registerProperty(ExplicitInterpolationProperty.PROPERTY); return timeline; diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java index 2a32cd41..c09002df 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java @@ -152,14 +152,17 @@ public abstract class GuiEditKeyframe> extends Abst inputs.setLayout(new VerticalLayout().setSpacing(10)).addElements(new VerticalLayout.Data(0.5, false), lensPanel.load(this.keyframe)); - link(lensPanel.focalDistanceField, lensPanel.apertureRadiusField, timeMinField, timeSecField, timeMSecField); + link(lensPanel.focalDistanceField, lensPanel.apertureRadiusField, lensPanel.fovField, timeMinField, timeSecField, timeMSecField); popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); } @Override protected Change save() { - return guiPathing.getMod().getCurrentTimeline().updateLensProperties(time, lensPanel.getFocalDistance(), lensPanel.getApertureRadius()); + SPTimeline timeline = guiPathing.getMod().getCurrentTimeline(); + Change lensChange = timeline.updateLensProperties(time, lensPanel.getFocalDistance(), lensPanel.getApertureRadius()); + Change fovChange = timeline.updateFov(time, lensPanel.getFov()); + return CombinedChange.createFromApplied(lensChange, fovChange); } @Override @@ -254,7 +257,7 @@ public abstract class GuiEditKeyframe> extends Abst lensPanel.load(this.keyframe); - link(xField, yField, zField, yawField, pitchField, rollField, timeMinField, lensPanel.focalDistanceField, lensPanel.apertureRadiusField, timeSecField, timeMSecField); + link(xField, yField, zField, yawField, pitchField, rollField, lensPanel.focalDistanceField, lensPanel.apertureRadiusField, lensPanel.fovField, timeMinField, timeSecField, timeMSecField); popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); } @@ -268,16 +271,17 @@ public abstract class GuiEditKeyframe> extends Abst ); Change lensChange = timeline.updateLensProperties(time, lensPanel.getFocalDistance(), lensPanel.getApertureRadius()); + Change fovChange = timeline.updateFov(time, lensPanel.getFov()); if (interpolationPanel.getSettingsPanel() == null) { // The last keyframe doesn't have interpolator settings because there is no segment following it - return CombinedChange.createFromApplied(positionChange, lensChange); + return CombinedChange.createFromApplied(positionChange, lensChange, fovChange); } Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator(); if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) { - return CombinedChange.createFromApplied(positionChange, lensChange, + return CombinedChange.createFromApplied(positionChange, lensChange, fovChange, timeline.setInterpolatorToDefault(time), timeline.setDefaultInterpolator(interpolator)); } else { - return CombinedChange.createFromApplied(positionChange, lensChange, + return CombinedChange.createFromApplied(positionChange, lensChange, fovChange, timeline.setInterpolator(time, interpolator)); } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiLensKeyframePanel.java b/src/main/java/com/replaymod/simplepathing/gui/GuiLensKeyframePanel.java index 44335e1f..2f240fcd 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiLensKeyframePanel.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiLensKeyframePanel.java @@ -1,6 +1,7 @@ package com.replaymod.simplepathing.gui; import com.replaymod.pathing.properties.LensProperties; +import com.replaymod.pathing.properties.FovProperty; import com.replaymod.replaystudio.pathing.path.Keyframe; import de.johni0702.minecraft.gui.container.AbstractGuiContainer; import de.johni0702.minecraft.gui.element.GuiLabel; @@ -13,12 +14,15 @@ public class GuiLensKeyframePanel extends AbstractGuiContainer=11600 "Mixin_AddIrisOdsShaderUniforms", "Mixin_LoadIrisOdsShaderPack",