Mirror joint movement

So, if you follow the animatie model tutorial for Unity, Nuitrack explains how to make a model follow the movements of the players etc. However, these movements are mirrored for the viewer. They’re technically correct but for someone who looks at it from the front it looks weird. If I raise my right hand in real life, it raises the right hand of the model, which is correct but weird because for the viewer it’s the hand on the left side.

I found a script somewhere on this forum but that just completely flips around the whole scene. Any idea how I can change the movements in code to make it like I want? If I raise my right hand in real life I want the left hand of the model to follow. NOTE: I already tried quite a lot including linking the right arm to the left one in the inspector etc. Everything I try either solves nothing or creates new problems

I currently use this code for movement:

void ProcessJoints()
{
for (int i = 0; i < _moveModelJoints.Length; i++)
{
_moveModelJoints[i].baseRotOffset = _moveModelJoints[i].bone.rotation;
jointsRigged.Add(_moveModelJoints[i].jointType, _moveModelJoints[i]);
}
}

    /// <summary>
    /// Used to position and follow the position/rotation of user
    /// </summary>
    /// <param name="skeleton">The skeleton to track</param>
    public void ProcessSkeleton(Skeleton skeleton)
    {
        //change the model position(torso position) & rotate 180 degrees on the y axis, otherwise the model will move in the opposite direction.
        //NOTE: For now we always put the Z axis on 0. This is to prevent any issues
        Vector3 torsoPos = Quaternion.Euler(0f, 180f, 0f) * (0.001f * skeleton.GetJoint(JointType.Torso).ToVector3());
        torsoPos.z = 0;
        transform.position = torsoPos;
    
        //Get information about each joint from the Nuitrack. Calculate the rotation of the model bone: take the 'mirrored' joint orientation, add the base rotation of the model bone.
        foreach (var riggedJoint in jointsRigged)
        {
            nuitrack.Joint joint = skeleton.GetJoint(riggedJoint.Key);
            MoveModelJointBehaviour moveModelJoint = riggedJoint.Value;
            Quaternion jointOrient = Quaternion.Inverse(CalibrationInfo.SensorOrientation) * (joint.ToQuaternionMirrored()) * moveModelJoint.baseRotOffset;
            moveModelJoint.bone.rotation = jointOrient;
        }
    }
1 Like

Like you said you can flip the whole screen. And that is what I recommend. Another easy fix is to place your depth camera behind the player instead if in the front.

You tried to rotate every joint but you need to flip the x axis relative to the torso or waist because most of the times that will be the center. Don’t try to flip the axis on the world 0.

Here is some example code that is not tested:

public GameObject[] JointObjects;    
public JointType[] usedJoints;    

public void Start(){
    jointObjects = new GameObject[usedJoints.Length -1]();
    for(int i = 0; i > usedJoints.Length-1; i++){
        jointObjects[i] = instantiate(new GameObject(), transform);
    }
}

public void FlipSkeleton(Skeleton skeleton){
    UnityEngine.Vector3 waistPosition= skeleton.GetJoint(JointType.Waist).ToVector3();
    
    int i = 0;
    foreach (nuitrack.Joint joint in OriginalSkeleton.Joints)
    {
        if (waistPosition.x - joint.Real.ToVector3().x > 0) {
            jointObject[i].transform.position = new UnityEngine.Vector3(Mathf.Abs(joint.Real.ToVector3().x), Mathf.Abs(joint.Real.ToVector3().y, Mathf.Abs(joint.Real.ToVector3().z);
        } else {
            jointObject[i].transform.position = new UnityEngine.Vector3(joint.Real.ToVector3().x * -1, Mathf.Abs(joint.Real.ToVector3().y, Mathf.Abs(joint.Real.ToVector3().z);
        }
        i++;
    }
}

i hope this works. I haven’t tested this but i think it should.

1 Like

Hi Ninja, thanks for explaining and helping. I’m not quite sure what you mean with your code. Would you mind explaining it a bit further and how I could apply it to my code I posted above?

Thanks a lot in advance :slight_smile:

Your code and mine a pretty the same. But the difference is that I flip the whole x axis relative to the waist of a skeleton to mirror it instead of rotating it.

In the start void I make gameObjects for every joint.

Then the flipskeleton void has the same function as your process skeleton void. First i get the waist x position and store that to later reference it. Then i loop through each joint in the skeleton that gets provided when you call the flipskeleton function. Then i check if the x axis of the joint is negative or positive relative to the waist. if its negative ill make it positive and if its positive ill make it negative. that way i flip it.

Now i think of it again i made a critical mistake. Like i said you must not flip it in the joints on the world axis but i did. So ill have the better adjusted code below:

 public GameObject[] JointObjects;    
 public JointType[] usedJoints;
 public GameObject waistObject; 
 
 public void Start(){
     jointObjects = new GameObject[usedJoints.Length -1]();
     for(int i = 0; i > usedJoints.Length-1; i++){
         jointObjects[i] = instantiate(new GameObject(), waistObject.transform);
     }
 }
 
 public void FlipSkeleton(Skeleton skeleton){
     UnityEngine.Vector3 waistPosition= skeleton.GetJoint(JointType.Waist).Real.ToVector3();
     waistObject.transform.position = waistPosition;
     int i = 0;
     foreach (nuitrack.Joint joint in OriginalSkeleton.Joints)
     {
         if (waistPosition.x - joint.Real.ToVector3().x > 0) {
             jointObject[i].transform.localPosition= new UnityEngine.Vector3(Mathf.Abs(joint.Real.ToVector3().x), Mathf.Abs(joint.Real.ToVector3().y, Mathf.Abs(joint.Real.ToVector3().z);
         } else {
             jointObject[i].transform.localPosition= new UnityEngine.Vector3(joint.Real.ToVector3().x * -1, Mathf.Abs(joint.Real.ToVector3().y, Mathf.Abs(joint.Real.ToVector3().z);
         }
         i++;
     }
 }

I added a waist object that will always be the center and the joint objects will change there local position to that waist object. You can ofcource change the output of the positions to what you like so you dont need to have a gameobject array.

1 Like

Thanks a lot! I’m going to try to work with this later. Appreciated :slight_smile:

I just found a way easier solution for this, and it’s right in the SDK… see bottom of this thread: Unity skeleton flip