Slerping the kool Aid - HELP!

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Slerping the kool Aid - HELP!

      (A lot of code follows, just warning everyone...)

      What's up guys.

      I've spent 16+ hours trying to wrap my head around quaternions and SLERP but haven't gotten very far. Using the following code I can get my AI character to patrol then instantly rotate to face the new patrol point:

      Source Code

      1. TransformComponent transformComponent = _actor.GetComponent<TransformComponent>();
      2. Vector3 position = transformComponent.Transform.Translation;
      3. Vector3 currentPatrolPoint = _patrolPoints.ElementAt(_currentPatrolPoint);
      4. Vector3 difference = (currentPatrolPoint - position);
      5. if (_hasFinishedRotating == false)
      6. {
      7. Vector3 lookAtDirection = Vector3.Normalize(difference);
      8. Vector3 forward = Vector3.Normalize(transformComponent.Transform.Forward);
      9. float forwardDot = (float)Math.Round(
      10. Vector3.Dot(
      11. forward,
      12. lookAtDirection),
      13. DOT_PRECISION);
      14. if (forwardDot != 1)
      15. {
      16. Vector3 rotationAxis = Vector3.Cross(lookAtDirection, forward);
      17. float rotationAngle = MathUtilities.GetUnsignedAngle(
      18. forward,
      19. lookAtDirection);
      20. Vector3 right = Vector3.Normalize(transformComponent.Transform.Right);
      21. float rightDot = (float)Math.Round(
      22. Vector3.Dot(
      23. right,
      24. lookAtDirection),
      25. DOT_PRECISION);
      26. if (rightDot > 0)
      27. {
      28. rotationAngle *= -1;
      29. }
      30. Matrix rotation = transformComponent.Transform;
      31. rotation *= Matrix.CreateFromAxisAngle(
      32. rotationAxis,
      33. rotationAngle);
      34. rotation.Translation = position;
      35. PhysicsComponent physicsComponent = _actor.GetComponent<PhysicsComponent>();
      36. physicsComponent.KinematicMove(rotation);
      37. }
      38. else
      39. {
      40. _hasFinishedRotating = true;
      41. }
      42. }
      Display All


      ..and here's MathUtilities.GetUnsignedAngle...

      Source Code

      1. public static float GetUnsignedAngle(
      2. Vector3 v1,
      3. Vector3 v2)
      4. {
      5. v1.Normalize();
      6. v2.Normalize();
      7. double angle = Math.Acos(
      8. Vector3.Dot(
      9. v1,
      10. v2));
      11. return (float)angle;
      12. }
      Display All


      ...but now I want my actor to slowly rotate (SLERP). Given everything I've read, the following should accomplish what I want to do...

      Source Code

      1. TransformComponent transformComponent = _actor.GetComponent<TransformComponent>();
      2. Vector3 position = transformComponent.Transform.Translation;
      3. Vector3 currentPatrolPoint = _patrolPoints.ElementAt(_currentPatrolPoint);
      4. Vector3 difference = (currentPatrolPoint - position);
      5. if (_isCurrentlyRotating == false)
      6. {
      7. Vector3 lookAtDirection = Vector3.Normalize(difference);
      8. Vector3 forward = Vector3.Normalize(transformComponent.Transform.Forward);
      9. float forwardDot = (float)Math.Round(
      10. Vector3.Dot(
      11. forward,
      12. lookAtDirection),
      13. DOT_PRECISION);
      14. if (forwardDot != 1)
      15. {
      16. Vector3 rotationAxis = Vector3.Cross(lookAtDirection, forward);
      17. float rotationAngle = MathUtilities.GetUnsignedAngle(
      18. forward,
      19. lookAtDirection);
      20. Vector3 right = Vector3.Normalize(transformComponent.Transform.Right);
      21. float rightDot = (float)Math.Round(
      22. Vector3.Dot(
      23. right,
      24. lookAtDirection),
      25. DOT_PRECISION);
      26. if (rightDot > 0)
      27. {
      28. rotationAngle *= -1;
      29. }
      30. Quaternion qStart = Quaternion.CreateFromRotationMatrix(transformComponent.Transform);
      31. Quaternion qEnd = Quaternion.CreateFromAxisAngle(rotationAxis, rotationAngle);
      32. Quaternion qSlerp = Quaternion.Slerp(
      33. qStart,
      34. qEnd,
      35. (float)gameTime.ElapsedGameTime.Milliseconds / 1000f);
      36. Matrix rotation = Matrix.CreateFromQuaternion(qSlerp);
      37. rotation.Translation = position;
      38. PhysicsComponent physicsComponent = _actor.GetComponent<PhysicsComponent>();
      39. physicsComponent.KinematicMove(rotation);
      40. }
      41. else
      42. {
      43. _isCurrentlyRotating = true;
      44. }
      45. }
      Display All


      ...this code rotates my actor CLOSE (maybe a tad more than half way) to the desired rotation but never actually gets there so my Actor just hangs. Can anyone offer any advice / improvements? It'd mean the world, thanks!

      Last note: I understand that SLERP won't fully rotate unless the slerpAmount is equal to 1 so though the above code doesn't reflect it, I've definitely tried setting the slerpAmount to 1 in the case that say the forwardDot was greater than some threshold. I just didn't include it here...
      -bullgoose

      The post was edited 12 times, last by bullgoose311 ().

    • This kind of thing is always a little tricky to debug with Visual Studio. What I typically like to do is craw debug lines (like the physics system does when it draws collision wirefames) to represent some of the vectors in your calculation, like the forward and difference vectors. Sometimes watching them move will give you a better idea of what is going on.

      But, not having that at my fingertips I'll take an educated guess at what is going on. I believe that your problem is likely in your third parameter to the Quaternion.Slerp, which you calculate as:

      Source Code

      1. (float)gameTime.ElapsedGameTime.Milliseconds / 1000f


      If the orientation of your AI character starts well in terms of rotation direction and speed, but stops short and hangs, it could be that when the result of the calculation grows past 1.0f, that the Quaternion.Slerp simply ignores those values, thus stopping the rotation short.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • What's up Mike thanks for the response.

      Yea I think your right, meaning I'll probably need to drop the AI portion of the system and move to starting a debug system then come back :)

      One thing I should have clarified is that in XNA gameTime.ElapsedGameTime is the elapsed game time since the last frame (not since the application started running) meaning the following calculation...

      (float)gameTime.ElapsedGameTime.Milliseconds / 1000f

      ...should never be greater than or equal to 1.0f (unless something is horribly wrong) which for the time being I have verified to not be a problem.

      (I"m thinking I'm going about slurp all wrong. Like maybe I should be incrementing my slurp amount each frame? Or possibly holding onto the initial rotation and or desired rotation instead of using the actor's new rotation every frame? Anyways...)

      Again thanks for the input!
      -bullgoose
    • Thanks for the input guys. I asked this question on gamedev and it turns out you need to multiply the starting and ending quaternions together for the desired rotation...

      Source Code

      1. Quaternion qStart = Quaternion.CreateFromRotationMatrix(transformComponent.Transform);
      2. Quaternion qEnd = Quaternion.CreateFromAxisAngle(rotationAxis, rotationAngle);
      3. Quaternion qSlerp = Quaternion.Slerp(
      4. qStart,
      5. qStart * qEnd,
      6. _slerpAmount);


      ...and though this works I don't necessarily understand why (not to mention my rotation axis wasn't normalized).

      After resolving this I ran into another issue where the rotation would slow down once it got close to the destination (on account of the way SLERP works). I fixed this by keeping track of the slerp amount in a private variable and incrementing it so the closer it got the bigger the slurp amount effectively giving the illusion that's it rotating at the same speed for the duration of the rotation.

      Thanks!
      -bullgoose

      The post was edited 2 times, last by bullgoose311 ().