From 8dde4203adb26a7fec5b50193d3ce7946903541c Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 21 Feb 2024 22:14:35 +0100 Subject: [PATCH] [VirtualInput] Add documentation for VirtualCameraAngleInput --- .../tasmod/virtual/VirtualInput.java | 117 +++++++++++++++--- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index bebbcfef..bab47891 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -11,6 +11,7 @@ import org.lwjgl.input.Mouse; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer; import com.minecrafttas.tasmod.mixin.playbackhooks.MixinMinecraft; import com.minecrafttas.tasmod.util.Ducks; import com.minecrafttas.tasmod.util.LoggerMarkers; @@ -478,7 +479,7 @@ public boolean isKeyDown(int keycode) { * If the key will be down and recognised in the next tick by Minecraft.
* This is equal to checking if a key on the physical mouse is pressed * @param keycode The keycode of the key in question - * @return Whether the key of the {@link #nextMouse} is down + * @return Whether the key of the {@link #nextMouse} is down3 */ public boolean willKeyBeDown(int keycode) { return nextMouse.isKeyDown(keycode); @@ -489,21 +490,70 @@ public boolean willKeyBeDown(int keycode) { /** * Subclass of {@link VirtualInput} handling camera angle logic.
*
- * Unlike {@link VirtualKeyboardInput} or {@link VirtualMouseInput} no subtick - * behaviour is implemented,
- * making this a simple pitch and yaw storing class, allowing for redirection. + * Normally, the camera angle is updated every frame.
+ * This meant that inputs on mouse and keyboard, updated every tick,
+ * had to sync with the camera angle, updated every frame, which desynced in some edgecases.
*
- * In theory, subtick behaviour is possible, but only useful for - * interpolation,
- * as the camera angle is only updated every tick (see - * {@link com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer}). + * After extensive testing, the decision was made to conform the camera to update every tick,
+ * instead of every frame. By itself, this meant that moving the camera felt really laggy
+ *
+ * Therefore an interpolation system was created that seperated the camera angle from the player head rotation,
+ * which are usually the synced. + *
+ * While the camera is the angle displayed on screen, the player rotation is responsible for the logic,
+ * e.g. at which block the player is currently aiming.
+ * This calls for a similar architecture as the {@link VirtualKeyboardInput} and the {@link VirtualMouseInput}. + *
+ * The {@link VirtualCameraAngleInput#currentCameraAngle} represents the player rotation, updated every tick
+ * and the {@link VirtualCameraAngleInput#nextCameraAngle} represents the camera angle, updated every frame. + *
+ * In pseudocode, the tick conformed camera looks like this: + *
+	 * public void runGameLoop() {// The main update loop, every frame
+	 * 	for(int i;i
+	 * Note that the camera is updated after the tick function.
+	 * Now in this pseudocode, the following methods from this class are injected like so:
+	 * 
+	 * public void runGameLoop() {// The main update loop, every frame
+	 * 	for(int i;i
+	 * 
+	 * While there is "subtick" behavior in this implementation,
+ * it is only used for the interpolation of the camera angle and not for the player rotation.
+ * This way you can customize the interpolated frames during playback. */ public class VirtualCameraAngleInput { /** - * The current camera angle + * The current camera angle in game.
+ *
+ * Updated every tick in {@link #nextCameraTick()} */ private final VirtualCameraAngle currentCameraAngle; + /** + * The new camera angle
+ *
+ * updated every frame in {@link #updateNextCameraAngle(float, float)}
+ * and updates {@link #currentCameraAngle} in {@link #nextCameraTick()} + */ private final VirtualCameraAngle nextCameraAngle = new VirtualCameraAngle(); + /** + * States of the {@link #nextCameraAngle} made during the tick.
+ * Is updated in {@link #nextCameraTick()} + */ private final List cameraAngleInterpolationStates = new ArrayList<>(); /** @@ -517,34 +567,71 @@ public VirtualCameraAngleInput(VirtualCameraAngle preloadedCamera) { } /** - * Update the camera angle + * Update the camera angle.
+ *
+ * Runs every frame * * @see com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer#runUpdate(float); - * @param pitch Absolute rotationPitch of the player - * @param yaw Absolute rotationYaw of the player + * @param pitchDelta Relative rotationPitch delta from LWJGLs mouse delta. + * @param yawDelta Relative rotationYaw delta from LWJGLs mouse delta. */ - public void updateNextCameraAngle(float pitch, float yaw) { + public void updateNextCameraAngle(float pitchDelta, float yawDelta) { // LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw); - nextCameraAngle.update(pitch, yaw); + nextCameraAngle.update(pitchDelta, yawDelta); } + /** + * Updates the {@link #currentCameraAngle} and {@link #cameraAngleInterpolationStates}.
+ * Runs every tick. + * + * @see MixinEntityRenderer#runUpdate(float) + */ public void nextCameraTick() { nextCameraAngle.getStates(cameraAngleInterpolationStates); currentCameraAngle.copyFrom(nextCameraAngle); } + /** + * Sets the camera coordinates directly.
+ *
+ * The camera angle is stored in absolute coordinates,
+ * which should match the vanilla coordinates displayed in F3
+ *
+ * This creates a problem when initializing the world, we don't know the camera angle the player has.
+ * Without this, the player would always start facing the 0;0 coordinate. + *
+ * To fix this, the camera is initialized with pitch and yaw being null. If that is the case,
+ * then the current player rotation is set with this method in {@link MixinEntityRenderer#runUpdate(float)} + * @param pitch The absolute player pitch + * @param yaw The absolute player yaw + */ public void setCamera(Float pitch, Float yaw) { nextCameraAngle.set(pitch, yaw); } + /** + * @return The absolute pitch coordinate of the player. May be null when it's initialized + */ public Float getCurrentPitch() { return currentCameraAngle.getPitch(); } - + + /** + * @return The absolute yaw coordinate of the player. May be null when it's initialized + */ public Float getCurrentYaw() { return currentCameraAngle.getYaw(); } + /** + * Gets the absolute coordinates of the camera angle + * + * @param partialTick The partial ticks of the timer + * @param pitch The original pitch of the camera + * @param yaw The original yaw of the camera + * @param enable Whether the custom interpolation is enabled. Enabled during playback. + * @return A triple of pitch, yaw and roll, as left, middle and right respectively + */ public Triple getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable) { if (!enable) { return Triple.of(nextCameraAngle.getPitch() == null ? pitch : nextCameraAngle.getPitch(), nextCameraAngle.getYaw() == null ? pitch : nextCameraAngle.getYaw() + 180, 0f);