From 1b18b5c952ff13d9af54d920147e9c4af29be69f Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 7 Dec 2021 16:08:44 +0100 Subject: [PATCH] Fix spectator hand jumping when rotating across 0 yaw boundary, take 2 The `yaw` value of non-client-players is constraint to [0; 360), so when that boundary is crossed, the `renderYaw` starts interpolating to its goal the incorrect way round (instead of crossing the 360 boundary as well). To fix that, we now add the difference between the current `renderYaw` and the desired one, modulo 360 (but with range (-180; 180]), to the current `renderYaw`. That way we always take the short way round. Additionally, because for rendering MC looks at the difference without modulo, afterwards (and when a new `yaw` is set) we wrap the result around such that the actual difference is always less than 180 and therefore rendered as intended. --- .../replaymod/replay/camera/CameraEntity.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 079f9740..5ae3fcec 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -248,6 +248,7 @@ public class CameraEntity this.lastRenderX = to.lastRenderX; this.lastRenderY = to.lastRenderY + yOffset; this.lastRenderZ = to.lastRenderZ; + this.wrapArmYaw(); updateBoundingBox(); } @@ -703,7 +704,35 @@ public class CameraEntity this.lastRenderYaw = this.renderYaw; this.lastRenderPitch = this.renderPitch; this.renderPitch = this.renderPitch + (this.pitch - this.renderPitch) * 0.5f; - this.renderYaw = this.renderYaw + (this.yaw - this.renderYaw) * 0.5f; + this.renderYaw = this.renderYaw + wrapDegrees(this.yaw - this.renderYaw) * 0.5f; + this.wrapArmYaw(); + } + + /** + * Minecraft renders the arm offset based on the difference between {@link #yaw} and {@link #renderYaw}. It does not + * wrap around the difference though, so if {@link #yaw} just wrapped around from 350 to 10 but {@link #renderYaw} + * is still at 355, then the difference will be inappropriately large. To fix this, we always wrap the + * {@link #renderYaw} such that it is no more than 180 degrees away from {@link #yaw}, even if that requires going + * outside the normal range. + */ + private void wrapArmYaw() { + this.renderYaw = wrapDegreesTo(this.renderYaw, this.yaw); + this.lastRenderYaw = wrapDegreesTo(this.lastRenderYaw, this.renderYaw); + } + + private static float wrapDegreesTo(float value, float towardsValue) { + while (towardsValue - value < -180) { + value -= 360; + } + while (towardsValue - value >= 180) { + value += 360; + } + return value; + } + + private static float wrapDegrees(float value) { + value %= 360; + return wrapDegreesTo(value, 0); } public boolean canSpectate(Entity e) {