From 2ee51a76e7549b6916843d9f4d383b7e59a11038 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 31 May 2023 20:24:12 +0200 Subject: [PATCH] fix yaw interpolation, use circle constants --- .../properties/rotation/YawRotation.kt | 4 +- .../data/entities/EntityRenderInfo.kt | 5 ++- .../minosoft/data/entities/EntityRotation.kt | 20 +++++++++ .../gui/rendering/input/CameraInput.kt | 12 +++--- .../skeletal/instance/SkeletalInstance.kt | 9 +++- .../gui/rendering/sky/planet/SunRenderer.kt | 3 +- .../protocol/protocol/ProtocolDefinition.java | 4 +- .../data/entities/EntityRotationTest.kt | 43 +++++++++++++++++++ 8 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 src/test/java/de/bixilon/minosoft/data/entities/EntityRotationTest.kt diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/rotation/YawRotation.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/rotation/YawRotation.kt index 0b068ef58..c21015c59 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/rotation/YawRotation.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/rotation/YawRotation.kt @@ -28,8 +28,8 @@ data class YawRotation( } companion object : EntityTargetPropertyFactory { - const val MIN = -180.0f - const val MAX = 180.0f + const val MIN = -EntityRotation.HALF_CIRCLE_DEGREE + const val MAX = EntityRotation.HALF_CIRCLE_DEGREE override val name: String = "y_rotation" private val parser = FloatRangeParser(null) diff --git a/src/main/java/de/bixilon/minosoft/data/entities/EntityRenderInfo.kt b/src/main/java/de/bixilon/minosoft/data/entities/EntityRenderInfo.kt index 32ad470db..fe16d21c9 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/EntityRenderInfo.kt +++ b/src/main/java/de/bixilon/minosoft/data/entities/EntityRenderInfo.kt @@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.entities import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kutil.math.interpolation.FloatInterpolation.interpolateLinear import de.bixilon.minosoft.data.Tickable +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.interpolateYaw import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.data.registries.shapes.aabb.AABB import de.bixilon.minosoft.gui.rendering.renderer.drawable.Drawable @@ -66,11 +67,11 @@ class EntityRenderInfo(private val entity: Entity) : Drawable, Tickable { private fun interpolateRotation(delta: Float) { val rotation1 = this.rotation1 - if (rotation === rotation1) { + if (rotation == rotation1) { return } val rotation0 = this.rotation0 - rotation = EntityRotation(interpolateLinear(delta, rotation0.yaw, rotation1.yaw), interpolateLinear(delta, rotation0.pitch, rotation1.pitch)) + rotation = EntityRotation(interpolateYaw(delta, rotation0.yaw, rotation1.yaw), interpolateLinear(delta, rotation0.pitch, rotation1.pitch)) } override fun draw(millis: Long) { diff --git a/src/main/java/de/bixilon/minosoft/data/entities/EntityRotation.kt b/src/main/java/de/bixilon/minosoft/data/entities/EntityRotation.kt index 7346faa2b..40f642b3b 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/EntityRotation.kt +++ b/src/main/java/de/bixilon/minosoft/data/entities/EntityRotation.kt @@ -16,6 +16,8 @@ import de.bixilon.kotlinglm.func.cos import de.bixilon.kotlinglm.func.rad import de.bixilon.kotlinglm.func.sin import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.kutil.math.interpolation.FloatInterpolation.interpolateLinear +import kotlin.math.abs data class EntityRotation( val yaw: Float, @@ -33,6 +35,24 @@ data class EntityRotation( } companion object { + const val CIRCLE_DEGREE = 360 + const val HALF_CIRCLE_DEGREE = 180 val EMPTY = EntityRotation(0.0f, 0.0f) + + + fun interpolateYaw(delta: Float, start: Float, end: Float): Float { + if (delta <= 0.0) return start + if (delta >= 1.0) return end + + var end = end + + if (abs(end - start) > HALF_CIRCLE_DEGREE) { + end += if (start > end) CIRCLE_DEGREE else -CIRCLE_DEGREE + } + + val i = interpolateLinear(delta, start, end) + + return (i + HALF_CIRCLE_DEGREE) % CIRCLE_DEGREE - HALF_CIRCLE_DEGREE + } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/CameraInput.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/CameraInput.kt index 0ac119bb0..086073df6 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/CameraInput.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/CameraInput.kt @@ -19,6 +19,8 @@ import de.bixilon.minosoft.config.key.KeyActions import de.bixilon.minosoft.config.key.KeyBinding import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.data.entities.EntityRotation +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.CIRCLE_DEGREE +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.HALF_CIRCLE_DEGREE import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.camera.MatrixHandler import de.bixilon.minosoft.input.camera.MovementInputActions @@ -114,12 +116,12 @@ class CameraInput( fun calculateRotation(delta: Vec2d, rotation: EntityRotation): EntityRotation { val delta = delta * 0.1f * controlsProfile.mouse.sensitivity var yaw = delta.x + rotation.yaw - if (yaw > 180) { - yaw -= 360 - } else if (yaw < -180) { - yaw += 360 + if (yaw > HALF_CIRCLE_DEGREE) { + yaw -= CIRCLE_DEGREE + } else if (yaw < -HALF_CIRCLE_DEGREE) { + yaw += CIRCLE_DEGREE } - yaw %= 180 + yaw %= HALF_CIRCLE_DEGREE val pitch = GLM.clamp(delta.y + rotation.pitch, -89.9, 89.9) return EntityRotation(yaw.toFloat(), pitch.toFloat()) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/SkeletalInstance.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/SkeletalInstance.kt index 471f6b2cb..cf0b856ef 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/SkeletalInstance.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/skeletal/instance/SkeletalInstance.kt @@ -75,8 +75,8 @@ class SkeletalInstance( fun updatePosition(position: Vec3d, rotation: EntityRotation) { val matrix = Mat4() .translateAssign(Vec3(position - context.camera.offset.offset)) - .rotateAssign((180.0f - rotation.yaw).rad, Vec3(0, 1, 0)) - .translateAssign(Vec3(-0.5, 0.0f, -0.5)) // move to bottom center + .rotateAssign((EntityRotation.HALF_CIRCLE_DEGREE - rotation.yaw).rad, Y_ROTATION_VECTOR) + .translateAssign(CENTER_OFFSET) // move to bottom center if (baseTransform != matrix) { baseTransform = matrix @@ -140,4 +140,9 @@ class SkeletalInstance( model.unload() } } + + companion object { + private val CENTER_OFFSET = Vec3(-0.5, 0.0f, -0.5) + private val Y_ROTATION_VECTOR = Vec3(0, 1, 0) + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/SunRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/SunRenderer.kt index 20813f043..2288e2730 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/SunRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/planet/SunRenderer.kt @@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.sky.planet import de.bixilon.kutil.hash.HashUtil.murmur64 import de.bixilon.kutil.random.RandomUtil.nextFloat +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.CIRCLE_DEGREE import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft import de.bixilon.minosoft.data.world.time.DayPhases import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer @@ -37,7 +38,7 @@ class SunRenderer( // 180: night (13k-23k) - return ((time.time / ProtocolDefinition.TICKS_PER_DAYf) - 0.25f) * 360.0f + return ((time.time / ProtocolDefinition.TICKS_PER_DAYf) - 0.25f) * CIRCLE_DEGREE } override fun calculateIntensity(): Float { diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java index b93b17aa4..7d33155ec 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java @@ -19,12 +19,14 @@ import de.bixilon.minosoft.data.text.formatting.color.RGBColor; import java.util.regex.Pattern; +import static de.bixilon.minosoft.data.entities.EntityRotation.CIRCLE_DEGREE; + public final class ProtocolDefinition { public static final int STRING_MAX_LENGTH = 32767; public static final int DEFAULT_PORT = 25565; public static final int SOCKET_TIMEOUT = 30000; public static final int STATUS_PROTOCOL_PACKET_MAX_SIZE = 1 << 16; - public static final float ROTATION_ANGLE_DIVIDER = 360.0F / 256.0F; + public static final float ROTATION_ANGLE_DIVIDER = CIRCLE_DEGREE / 256.0F; public static final float SOUND_PITCH_DIVIDER = 100.0F / 63.0F; diff --git a/src/test/java/de/bixilon/minosoft/data/entities/EntityRotationTest.kt b/src/test/java/de/bixilon/minosoft/data/entities/EntityRotationTest.kt new file mode 100644 index 000000000..09052c50d --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/data/entities/EntityRotationTest.kt @@ -0,0 +1,43 @@ +package de.bixilon.minosoft.data.entities + +import de.bixilon.minosoft.data.entities.EntityRotation.Companion.interpolateYaw +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class EntityRotationTest { + + @Test + fun interpolation1() { + assertEquals(50.0f, interpolateYaw(0.5f, 0.0f, 100.0f)) + } + + @Test + fun interpolation2() { + assertEquals(0.0f, interpolateYaw(-1.0f, 0.0f, 100.0f)) + } + + @Test + fun interpolation3() { + assertEquals(100.0f, interpolateYaw(2.0f, 0.0f, 100.0f)) + } + + @Test + fun interpolation4() { + assertEquals(-180.0f, interpolateYaw(0.1f, 180.0f, -180.0f)) + } + + @Test + fun interpolation5() { + assertEquals(-180.0f, interpolateYaw(0.9f, 180.0f, -180.0f)) + } + + @Test + fun interpolation6() { + assertEquals(-170.0f, interpolateYaw(0.5f, 180.0f, -160.0f)) + } + + @Test + fun interpolation7() { + assertEquals(150.0f, interpolateYaw(0.5f, 110.0f, -170.0f)) + } +}