Search Unity

Quaternion Wizardry for ConfigurableJoint

Discussion in 'Scripting' started by MatthewW, Feb 14, 2008.

  1. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,355
    We're using the targetRotation on a ConfigurableJoint to mimic an animation. We need help with some angle stuff.

    - It seems targetRotation is relative to the initial rotation of an object. A target of Quaternion.identity will not move the object to match its parent. It will move it to match its initial position.

    - At any time, we know the object's localRotation, and the localRotation of the object we want to mimic.

    - How do we describe the target's local rotation relative to our initial position?

    In simple terms, let's say we have an object with:

    - Initial local rotation of 30 degrees
    - Our target's local rotation is 40 degrees
    - So our joint's target rotation should be 10 degrees

    But what is this in Quaternion math? This nearly works, but is backwards on one axis:

    Code (csharp):
    1.  
    2. jointC.targetRotation =   target.localRotation * Quaternion.Inverse(initialRotation);
    Help, oh great wizards!
     
  2. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,355
    This logic works. Seems like a hack though--so now I'm curious what the "pure" math is:

    Code (csharp):
    1.  
    2. var from:Vector3 = target.localRotation * Vector3.forward;
    3. var to:Vector3 = initialRotation * Vector3.forward;
    4.    
    5. jointC.targetRotation = Quaternion.FromToRotation(from, to);
    6.  
     
  3. simonal

    simonal

    Joined:
    Apr 17, 2009
    Posts:
    16
    Did you get any clarity on this issue?

    Shouldn't the targetRotation be relative to the joints local frame set up by joint axis and secondray axis? My gues is that your solution won't work if you change the joints axis/secondary axis.
     
  4. mstevenson

    mstevenson

    Joined:
    Sep 24, 2009
    Posts:
    168
    I hate to dig up ancient threads, but since my investigation started here I may as well post a complete solution.

    I've created a set of extension methods for computing a ConfigurableJoint's targetRotation from a local or world rotation. There are two very important ConfigurableJoint parameters that most available solutions ignore: axis and secondary axis. Together these produce a unique joint coordinate space. Values in this space must be converted to local or world space before transformations are applied. The resulting value must then be converted back to joint space before being assigned to targetRotation.

    The joint coordinate space is unusual: its axes are inverted, and it always begins with a rotation of Quaternion.identity (no rotation at all) regardless of its transform's local or world rotation. This is nothing that some careful quaternion math can't solve.

    I've tested these methods under a variety of conditions and have not been able to get them to fail. This solution fully supports arbitrary ConfigurableJoint axes while allowing rotations to be expressed in a transform's local or world space.

    I'll be maintaining the code in a public Gist: https://gist.github.com/mstevenson/4958837

    ConfigurableJointExtensions.cs:

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public static class ConfigurableJointExtensions {
    4.     /// <summary>
    5.     /// Sets a joint's targetRotation to match a given local rotation.
    6.     /// The joint transform's local rotation must be cached on Start and passed into this method.
    7.     /// </summary>
    8.     public static void SetTargetRotationLocal (this ConfigurableJoint joint, Quaternion targetLocalRotation, Quaternion startLocalRotation)
    9.     {
    10.         if (joint.configuredInWorldSpace) {
    11.             Debug.LogError ("SetTargetRotationLocal should not be used with joints that are configured in world space. For world space joints, use SetTargetRotation.", joint);
    12.         }
    13.         SetTargetRotationInternal (joint, targetLocalRotation, startLocalRotation, Space.Self);
    14.     }
    15.    
    16.     /// <summary>
    17.     /// Sets a joint's targetRotation to match a given world rotation.
    18.     /// The joint transform's world rotation must be cached on Start and passed into this method.
    19.     /// </summary>
    20.     public static void SetTargetRotation (this ConfigurableJoint joint, Quaternion targetWorldRotation, Quaternion startWorldRotation)
    21.     {
    22.         if (!joint.configuredInWorldSpace) {
    23.             Debug.LogError ("SetTargetRotation must be used with joints that are configured in world space. For local space joints, use SetTargetRotationLocal.", joint);
    24.         }
    25.         SetTargetRotationInternal (joint, targetWorldRotation, startWorldRotation, Space.World);
    26.     }
    27.    
    28.     static void SetTargetRotationInternal (ConfigurableJoint joint, Quaternion targetRotation, Quaternion startRotation, Space space)
    29.     {
    30.         // Calculate the rotation expressed by the joint's axis and secondary axis
    31.         var right = joint.axis;
    32.         var forward = Vector3.Cross (joint.axis, joint.secondaryAxis).normalized;
    33.         var up = Vector3.Cross (forward, right).normalized;
    34.         Quaternion worldToJointSpace = Quaternion.LookRotation (forward, up);
    35.        
    36.         // Transform into world space
    37.         Quaternion resultRotation = Quaternion.Inverse (worldToJointSpace);
    38.        
    39.         // Counter-rotate and apply the new local rotation.
    40.         // Joint space is the inverse of world space, so we need to invert our value
    41.         if (space == Space.World) {
    42.             resultRotation *= startRotation * Quaternion.Inverse (targetRotation);
    43.         } else {
    44.             resultRotation *= Quaternion.Inverse (targetRotation) * startRotation;
    45.         }
    46.        
    47.         // Transform back into joint space
    48.         resultRotation *= worldToJointSpace;
    49.        
    50.         // Set target rotation to our newly calculated rotation
    51.         joint.targetRotation = resultRotation;
    52.     }
    53. }
    Usage:

    Code (csharp):
    1.  
    2. Quaternion initialRotation;
    3. ConfigurableJoint joint;
    4.  
    5. void Awake ()
    6. {
    7.     joint = GetComponent<ConfigurableJoint> ();
    8.     initialRotation = joint.transform.localRotation;
    9. }
    10.  
    11. void Start ()
    12. {
    13.     joint.SetTargetRotationLocal (Quaternion.Euler (0, 90, 0), initialRotation);
    14. }
    15.  
     
    Last edited: Feb 15, 2013
  5. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    285
    I've noticed that this code doesn't work for when the configurable joint is connecting two rigidbodies together.
     
  6. kookyoo

    kookyoo

    Joined:
    Apr 19, 2010
    Posts:
    46
unityunity