Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Quaternion to remove pitch

Discussion in 'Scripting' started by syjgin, Feb 5, 2020.

  1. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    It must be simple, but I still can not create such quaternion. After user rotated flying object around it's local left axis
    Code (CSharp):
    1. transform.localToWorldMatrix.MultiplyVector(Vector3.left))
    to produce pitch angle, I need to smoothly return this angle to zero.
    That's how I'm trying to do this:
    Code (CSharp):
    1.  
    2.         private void Update()
    3.         {
    4.             if (_targetRotation != _rigidBody.rotation)
    5.             {
    6.                 //rotating by user defined target or normalize pitch
    7.                 _rigidBody.MoveRotation(Quaternion.Lerp(_rigidBody.rotation, _targetRotation, MovementConfig.RotationVelocity * Time.time));
    8.             } else {
    9.                 if(_rigidBody.rotation.x != _initialRotation.x) {
    10.                     //create zero rotation.x target quaternion
    11.                     Quaternion delta = Quaternion.Inverse(Quaternion.AngleAxis(360 - _rigidBody.rotation.x, transform.localToWorldMatrix.MultiplyVector(Vector3.left)));
    12.                     _targetRotation = delta * _rigidBody.rotation;  
    13.                 }
    14.             }
    15.         }
    But this is not works. How to create quaternion to nullify that rotation?
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    rotation.x
    is not what you think it is and you should basically NEVER access it directly for any reason. (Internally, Quaternions are represented by four parameters, w x y z, but they don't have intuitive meanings and you shouldn't get or set them directly.)

    You appear to be thinking of
    rotation.eulerAngles.x
    but even that is not really safe to use in this way, because Euler angles are not truly independent; there's more than one set of Euler angles that will give the same final orientation. For instance, an object that rolls onto its right side and then turns "left" will end up facing up into the air in absolute terms even though it never rotated "upwards" in its local space.

    In abstract terms, what you probably want is to project the object's forward axis onto the XZ plane and then calculate the rotation necessary to align the forward axis with that projection. (Notice this is ambiguous if the object is looking directly up or down, so you'll need a special case for that.) You can probably do that with Vector3.ProjectOnPlane and Quaternion.FromToRotation.
     
    syjgin likes this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    We really should write a 'Quaternion' thread and pin it to the top... lol.
     
    deus0 likes this.
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    If I could go back in time, I'd recommend making the public interface for w x y z on Quaternion be something less obvious and harder to mistake for Euler angles. Maybe require an explicit cast to/from Vector4 in order to access them.

    Might even be worth changing in a future version. It would be a breaking change, but only for the tiny percentage of people who have a valid reason to be accessing those directly.
     
    StarManta likes this.
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,741
    I would happily give permission for my Euler Angles article to be used as the sticky post and/or rewritten to suit that.
     
    lordofduct likes this.
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Agreed. Though I wouldn't require a cast (why slow down access if you need it?).

    Maybe instead of using xyzw, use ijkw, or ijk and whatever for the real component. ijk being the common names for the 3 complex axes of quats out in the math world. Most people wouldn't say "rotation.i" and think "oh, that's rotation around the x-axis" since well... it's not, and it doesn't read like it is. And anyone familiar with quats would know exactly what that means.
     
    orionsyndrome and StarManta like this.
  7. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Ok, now it seems to be working:
    Code (CSharp):
    1. private void Update()
    2.         {
    3.             if (_targetRotation != _rigidBody.rotation)
    4.             {
    5.                 _rigidBody.MoveRotation(Quaternion.Lerp(_rigidBody.rotation, _targetRotation, MovementConfig.RotationVelocity * Time.time));
    6.             } else {
    7.                 float verticalAngle = Vector3.Angle(transform.forward, Quaternion.AngleAxis(_rigidBody.rotation.eulerAngles.y, Vector3.up) * Vector3.forward);
    8.                 Debug.Log("vertical angle:" + verticalAngle);
    9.                 if(Mathf.Abs(verticalAngle) > 1) {
    10.                     Quaternion delta = Quaternion.FromToRotation(transform.forward, Quaternion.AngleAxis(_rigidBody.rotation.eulerAngles.y, Vector3.up) * Vector3.forward);
    11.                     _targetRotation = delta * _rigidBody.rotation;
    12.                 }
    13.             }
    14.         }
    Thank you!
     
  8. Spikee_wave

    Spikee_wave

    Joined:
    Feb 27, 2018
    Posts:
    5
    Just sharing if someone in the future will need this. Methods to get Quaternion rotation of one axis without need to convert to euler angles and back.

    Code (CSharp):
    1.  
    2. public static Quaternion GetXAxisRotation(this Quaternion quaternion)
    3.     {
    4.         float a = Mathf.Sqrt((quaternion.w * quaternion.w) + (quaternion.x * quaternion.x));
    5.         return new Quaternion(x: quaternion.x, y: 0, z: 0, w: quaternion.w / a);
    6.  
    7.     }
    8.  
    9.     public static Quaternion GetYAxisRotation(this Quaternion quaternion)
    10.     {
    11.         float a = Mathf.Sqrt((quaternion.w * quaternion.w) + (quaternion.y * quaternion.y));
    12.         return new Quaternion (x: 0, y: quaternion.y, z: 0, w: quaternion.w / a);
    13.  
    14.     }
    15.  
    16.     public static Quaternion GetZAxisRotation(this Quaternion quaternion)
    17.     {
    18.         float a = Mathf.Sqrt((quaternion.w * quaternion.w) + (quaternion.z * quaternion.z));
    19.         return new Quaternion(x: 0, y: 0, z: quaternion.z, w: quaternion.w / a);
    20.     }
     
    Last edited: Sep 10, 2021