|
3 | 3 | import java.util.ArrayList; |
4 | 4 | import java.util.List; |
5 | 5 |
|
6 | | -import org.apache.commons.lang3.tuple.Triple; |
7 | | - |
8 | 6 | import com.minecrafttas.tasmod.TASmodClient; |
9 | 7 | import com.minecrafttas.tasmod.events.EventVirtualInput; |
| 8 | +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; |
10 | 9 | import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck; |
11 | 10 | import com.minecrafttas.tasmod.util.PointerNormalizer; |
12 | 11 |
|
|
15 | 14 |
|
16 | 15 | public class VirtualInterpolationHandler implements EventVirtualInput.EventVirtualMouseTick, EventVirtualInput.EventVirtualCameraAngleTick { |
17 | 16 |
|
18 | | - private final List<VirtualMouse> mousePointerInterpolationStates = new ArrayList<>(); |
19 | 17 | /** |
20 | | - * States of the {@link #nextCameraAngle} made during the tick.<br> |
21 | | - * Is updated in {@link #nextCameraTick()} |
| 18 | + * Copy of the {@link PlaybackControllerClient#nextPlaybackMouse} |
22 | 19 | */ |
23 | | - private final List<VirtualCameraAngle> cameraAngleInterpolationStates = new ArrayList<>(); |
24 | | - |
25 | 20 | private VirtualMouse nextMouse = new VirtualMouse(); |
| 21 | + /** |
| 22 | + * Copy of the {@link VirtualInput#CAMERA_ANGLE#nextCameraAngle} |
| 23 | + */ |
26 | 24 | private VirtualCameraAngle nextCameraAngle = new VirtualCameraAngle(); |
27 | 25 |
|
28 | | - public int getInterpolatedX(float partialTick, boolean enable) { |
29 | | - |
30 | | - int interpolatedPointerX = nextMouse.getCursorX(); |
31 | | - |
32 | | - if (enable && !mousePointerInterpolationStates.isEmpty()) { |
33 | | - int index = (int) MathHelper.clampedLerp(0, mousePointerInterpolationStates.size() - 1, partialTick); // Get interpolate index |
34 | | - |
35 | | - VirtualMouse interpolatedCamera = mousePointerInterpolationStates.get(index); |
36 | | - |
37 | | - interpolatedPointerX = interpolatedCamera.getCursorX(); |
38 | | - |
39 | | - } |
40 | | - Minecraft mc = Minecraft.getMinecraft(); |
41 | | - GuiScreenDuck gui = (GuiScreenDuck) mc.currentScreen; |
| 26 | + /** |
| 27 | + * States of the {@link #nextMouse} made during the tick.<br> |
| 28 | + * Is updated in {@link #onVirtualMouseTick()} |
| 29 | + */ |
| 30 | + private final List<VirtualMouse> mousePointerStates = new ArrayList<>(); |
| 31 | + /** |
| 32 | + * States of the {@link #nextCameraAngle} made during the tick.<br> |
| 33 | + * Is updated in {@link #onVirtualCameraTick()} |
| 34 | + */ |
| 35 | + private final List<VirtualCameraAngle> cameraAngleStates = new ArrayList<>(); |
| 36 | +// private int debugFinalIndex = 0; |
42 | 37 |
|
43 | | - if (gui != null) { |
44 | | - interpolatedPointerX = gui.rescaleX(PointerNormalizer.reapplyScalingX(interpolatedPointerX)); |
45 | | - } |
| 38 | + @Override |
| 39 | + public VirtualMouse onVirtualMouseTick(VirtualMouse vmouse) { |
| 40 | + this.nextMouse = vmouse; |
| 41 | +// if (TASmodClient.controller.isPlayingback()) { |
| 42 | +// System.out.println(debugFinalIndex == mousePointerInterpolationStates.size() - 1); |
| 43 | +// } |
| 44 | + mousePointerStates.clear(); |
| 45 | + TASmodClient.controller.getNextMouse().getStates(mousePointerStates); |
| 46 | + return null; |
| 47 | + } |
46 | 48 |
|
47 | | - return interpolatedPointerX; |
| 49 | + @Override |
| 50 | + public VirtualCameraAngle onVirtualCameraTick(VirtualCameraAngle vcamera) { |
| 51 | + this.nextCameraAngle = vcamera; |
| 52 | + cameraAngleStates.clear(); |
| 53 | + nextCameraAngle.getStates(cameraAngleStates); |
| 54 | + return null; |
48 | 55 | } |
49 | 56 |
|
50 | | - public int getInterpolatedY(float partialTick, boolean enable) { |
| 57 | + /** |
| 58 | + * Interpolates the mouse cursor inbetween ticks based on the data from the next tick |
| 59 | + * |
| 60 | + * @param partialTick The partial ticks used for interpolating |
| 61 | + * @param enable If the interpolation should be enabled. Basically if {@link PlaybackControllerClient#isPlayingback()} |
| 62 | + * @return A {@link MouseInterpolation} object with x and y coordinates |
| 63 | + */ |
| 64 | + public MouseInterpolation getInterpolatedMouseCursor(float partialTick, boolean enable) { |
51 | 65 |
|
| 66 | + int interpolatedPointerX = nextMouse.getCursorX(); |
52 | 67 | int interpolatedPointerY = nextMouse.getCursorY(); |
53 | 68 |
|
54 | | - if (enable && !mousePointerInterpolationStates.isEmpty()) { |
55 | | - int index = (int) MathHelper.clampedLerp(0, mousePointerInterpolationStates.size() - 1, partialTick); // Get interpolate index |
56 | | - |
57 | | - VirtualMouse interpolatedCamera = mousePointerInterpolationStates.get(index); |
| 69 | + if (enable && !mousePointerStates.isEmpty()) { |
| 70 | + partialTick = dynamicallyRound(partialTick, TASmodClient.tickratechanger.ticksPerSecond); |
| 71 | + int index = (int) MathHelper.clampedLerp(0, mousePointerStates.size() - 1, partialTick); // Get interpolate index |
| 72 | +// debugFinalIndex = index; |
| 73 | + VirtualMouse interpolatedCamera = mousePointerStates.get(index); |
58 | 74 |
|
| 75 | + interpolatedPointerX = interpolatedCamera.getCursorX(); |
59 | 76 | interpolatedPointerY = interpolatedCamera.getCursorY(); |
60 | 77 |
|
61 | 78 | } |
62 | | - |
63 | 79 | Minecraft mc = Minecraft.getMinecraft(); |
64 | 80 | GuiScreenDuck gui = (GuiScreenDuck) mc.currentScreen; |
65 | 81 |
|
66 | 82 | if (gui != null) { |
| 83 | + interpolatedPointerX = gui.rescaleX(PointerNormalizer.reapplyScalingX(interpolatedPointerX)); |
67 | 84 | interpolatedPointerY = gui.rescaleY(PointerNormalizer.reapplyScalingY(interpolatedPointerY)); |
68 | 85 | } |
69 | 86 |
|
70 | | - return interpolatedPointerY; |
| 87 | + return new MouseInterpolation(interpolatedPointerX, interpolatedPointerY); |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * Rounds the partial tick to 1 depending on the tickrate |
| 92 | + * |
| 93 | + * To correctly play back the mouse cursor, the partial ticks have to reach 1 at some point. |
| 94 | + * However this is not the case in higher tickrates. |
| 95 | + * The solution is to round the partial ticks to 1 after a certain threshold. |
| 96 | + * |
| 97 | + * The higher the tps, the lower the threshold for rounding. |
| 98 | + * |
| 99 | + * @param partialTick The partial ticks to round |
| 100 | + * @param tps The ticks per second used for setting the threshold |
| 101 | + * @return The rounded partial ticks |
| 102 | + */ |
| 103 | + private float dynamicallyRound(float partialTick, float tps) { |
| 104 | + float percent = tps / 100; |
| 105 | + if (partialTick > 1 - percent) |
| 106 | + partialTick = 1; |
| 107 | + return partialTick; |
71 | 108 | } |
72 | 109 |
|
73 | 110 | /** |
74 | | - * Gets the absolute coordinates of the camera angle |
| 111 | + * Gets the interpolated coordinates of the camera angle |
75 | 112 | * |
76 | 113 | * @param partialTick The partial ticks of the timer |
77 | 114 | * @param pitch The original pitch of the camera |
78 | 115 | * @param yaw The original yaw of the camera |
79 | 116 | * @param enable Whether the custom interpolation is enabled. Enabled during playback. |
80 | 117 | * @return A triple of pitch, yaw and roll, as left, middle and right respectively |
81 | 118 | */ |
82 | | - public Triple<Float, Float, Float> getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable) { |
| 119 | + public CameraInterpolation getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable) { |
83 | 120 |
|
84 | 121 | float interpolatedPitch = nextCameraAngle.getPitch() == null ? pitch : nextCameraAngle.getPitch(); |
85 | 122 | float interpolatedYaw = nextCameraAngle.getYaw() == null ? yaw : nextCameraAngle.getYaw() + 180; |
86 | 123 |
|
87 | | - if (enable && !cameraAngleInterpolationStates.isEmpty()) { |
88 | | - int index = (int) MathHelper.clampedLerp(0, cameraAngleInterpolationStates.size() - 1, partialTick); // Get interpolate index |
| 124 | + if (enable && !cameraAngleStates.isEmpty()) { |
| 125 | + int index = (int) MathHelper.clampedLerp(0, cameraAngleStates.size() - 1, partialTick); // Get interpolate index |
89 | 126 |
|
90 | | - VirtualCameraAngle interpolatedCamera = cameraAngleInterpolationStates.get(index); |
| 127 | + VirtualCameraAngle interpolatedCamera = cameraAngleStates.get(index); |
91 | 128 |
|
92 | 129 | interpolatedPitch = interpolatedCamera.getPitch() == null ? 0 : interpolatedCamera.getPitch(); |
93 | 130 | interpolatedYaw = interpolatedCamera.getYaw() == null ? 0 : interpolatedCamera.getYaw() + 180; |
94 | 131 |
|
95 | 132 | } |
96 | | - return Triple.of(interpolatedPitch, interpolatedYaw, 0f); |
| 133 | + return new CameraInterpolation(interpolatedPitch, interpolatedYaw); |
97 | 134 | } |
98 | 135 |
|
99 | | - @Override |
100 | | - public VirtualMouse onVirtualMouseTick(VirtualMouse vmouse) { |
101 | | - this.nextMouse = vmouse; |
102 | | - mousePointerInterpolationStates.clear(); |
103 | | - TASmodClient.controller.getNextMouse().getStates(mousePointerInterpolationStates); |
104 | | - return null; |
| 136 | + public static class MouseInterpolation { |
| 137 | + final Integer x; |
| 138 | + final Integer y; |
| 139 | + |
| 140 | + public MouseInterpolation(Integer x, Integer y) { |
| 141 | + this.x = x; |
| 142 | + this.y = y; |
| 143 | + } |
| 144 | + |
| 145 | + public Integer getX() { |
| 146 | + return x; |
| 147 | + } |
| 148 | + |
| 149 | + public Integer getY() { |
| 150 | + return y; |
| 151 | + } |
| 152 | + |
| 153 | + @Override |
| 154 | + public boolean equals(Object obj) { |
| 155 | + if (obj instanceof MouseInterpolation) { |
| 156 | + MouseInterpolation other = (MouseInterpolation) obj; |
| 157 | + return this.x.equals(other.x) && this.y.equals(other.y); |
| 158 | + } |
| 159 | + return super.equals(obj); |
| 160 | + } |
105 | 161 | } |
106 | 162 |
|
107 | | - @Override |
108 | | - public VirtualCameraAngle onVirtualCameraTick(VirtualCameraAngle vcamera) { |
109 | | - this.nextCameraAngle = vcamera; |
110 | | - cameraAngleInterpolationStates.clear(); |
111 | | - nextCameraAngle.getStates(cameraAngleInterpolationStates); |
112 | | - return null; |
| 163 | + public static class CameraInterpolation { |
| 164 | + final Float pitch; |
| 165 | + final Float yaw; |
| 166 | + |
| 167 | + public CameraInterpolation(Float pitch, Float yaw) { |
| 168 | + this.pitch = pitch; |
| 169 | + this.yaw = yaw; |
| 170 | + } |
| 171 | + |
| 172 | + public Float getPitch() { |
| 173 | + return pitch; |
| 174 | + } |
| 175 | + |
| 176 | + public Float getYaw() { |
| 177 | + return yaw; |
| 178 | + } |
| 179 | + |
| 180 | + @Override |
| 181 | + public boolean equals(Object obj) { |
| 182 | + if (obj instanceof CameraInterpolation) { |
| 183 | + CameraInterpolation other = (CameraInterpolation) obj; |
| 184 | + return this.pitch.equals(other.pitch) && this.yaw.equals(other.yaw); |
| 185 | + } |
| 186 | + return super.equals(obj); |
| 187 | + } |
113 | 188 | } |
114 | 189 | } |
0 commit comments