4

Currently I am trying to write a script that will mirror the movements of one controller onto the other for users who only have 1 functioning arm. How do I mirror the position of the one controller onto the other that way the arms are in bilateral movement?

Mirroring the y axis and z axis were easy since they move together and the rotation was easy to mirror. I am unable to mirror the x axis movement. I want it so that if the one hand moves out the other does the same, and they both move in together. Any ideas how I may be able to do this? I have attached my current script. I also disabled the position tracking of the non mirrored controller with simple Boolean logic in the OVRCemeraRig script to prevent stutters in movement Need to use OVRCemeraRig since using final IK

I have tried taking a difference in the x positions in the working arm and then adding that value to the none working arm. That did not work.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VRMirror : MonoBehaviour
{
    public bool mirrorLeft;
    public bool mirrorRight;
    public GameObject leftHand; //left hand anchor in OVRCameraRig
    public GameObject rightHand; //right hand anchor from OVRCameraRig
    void Start()
    {

    }
    void FixedUpdate()
    {
        Transform left = leftHand.GetComponent<Transform>();
        Transform right = rightHand.GetComponent<Transform>();
        if (mirrorLeft)
        {
            Vector3 leftPos = left.position;
            Quaternion leftRot = left.rotation;
            leftRot.y = -leftRot.y;
            right.position = leftPos;
            right.rotation = leftRot;
        }
        else if (mirrorRight)
        {
            Vector3 rightPos = right.position;
            Quaternion rightRot = right.rotation;

            rightRot.y = -rightRot.y;
            left.position = rightPos;
            left.rotation = rightRot;

        }
    }
}
Afroca
  • 115
  • 11

2 Answers2

1

For the sake of robustness, let's assume your player's body's rotation might not necessarily always have its right pointing in the (1,0,0) world direction. Instead, we can get a reference to the player's Transform, playerTransform, (be sure to assign it using the inspector or in Start if you must) and make our calculations using that.

To calculate the bilateral symmetric position for a relative vector, you can calculate relativeVec - 2f * playerTransform.right * Vector3.Dot(relativeVec, playerTransform.right);. Explanation for why that works is in the comments.

For position, we can convert the absolute position of the source hand to be relative to the player's position, then find the relative position of the destination hand, then convert that back into an absolute position.

For rotation, determine up & forward for the source hand and reflect them to determine the up & forward for the destination hand. Use Quaternion.SetLookRotation to convert the vectors to the rotation for the destination hand.

We can use the same code for relative positions and for our direction vectors, so it actually doesn't take much code once you have the math. And also, since Transform is a class, we can make one method that does the reflection procedure, and then pass into it which transforms we want to be the source and destination:

public class VRMirror : MonoBehaviour
{
    public bool mirrorLeft;
    public bool mirrorRight;
    public GameObject leftHand; //left hand anchor in OVRCameraRig
    public GameObject rightHand; //right hand anchor from OVRCameraRig

    public Transform playerTransform;

    void Start()
    {

    }
    void FixedUpdate()
    {

        Transform left = leftHand.GetComponent<Transform>();
        Transform right = rightHand.GetComponent<Transform>();
        if (mirrorLeft)
        {
            MirrorFromTo(left, right);
        }
        else if (mirrorRight)
        {
            MirrorFromTo(right, left);
        }
    }

    void MirrorFromTo(Transform sourceTransform, Transform destTransform)
    {
        // Determine dest position
        Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
        Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
        destTransform.position = playerTransform.position + playerToDestHand ;

        // Determine dest rotation
        Vector3 forwardVec = ReflectRelativeVector(sourceTransform.forward);
        Vector3 upVec = ReflectRelativeVector(sourceTransform.up);
        destTransform.rotation = Quaternion.LookRotation(forwardVec,upVec);
    }

    Vector3 ReflectRelativeVector(Vector3 relativeVec) 
    {
       // relativeVec
       //     Take the relative vector....
       // + Vector3.Dot(relativeVec, playerTransform.right)
       //     and for how far along the player's right direction it is 
       //     away from the player (may be negative),
       // * playerTransform.right
       //     move it that distance along the player's right...
       // * -2f
       //    negative two times (i.e., along the left direction 2x)

       return relativeVec 
           + Vector3.Dot(relativeVec, playerTransform.right)
               * playerTransform.right 
               * -2f;
    }
}
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • 1
    What did you use as the playerTransform? I am using the player model and it seems to work. Except for the wrist rotations. When I supinate the the one wrist I was the other to supinate as well. Currently only one wrist moves with your code. With my code I was able to get the wrists to mirror except for supination and pronation. Those were inverted. – Afroca Jul 15 '19 at 15:27
  • 1
    I copied and pasted exactly what you had. The issue with your code was there was no wrist rotations at all. You are using a Vector3 to perform rotations when it should be a Quaternion. I am not sure if that may have been the issue. I answered the question with the minor alterations I made and it works perfectly. – Afroca Jul 15 '19 at 16:04
  • 1
    @Afroca oh actually I know what's wrong... silly me. Using `SetLookRotation` was modifying a copy of the rotation. Gonna change the line to `destTransform.rotation = Quaternion.LookRotation(forwardVec,upVec);` That should do it assuming your left hand's local axes are like [this](https://i.imgur.com/e4oXNah.png) and your right hand just has the middle finger going `-x` – Ruzihm Jul 15 '19 at 16:11
  • 1
    I just ran the code with the changes you made and it work! Thank you so much! – Afroca Jul 15 '19 at 17:17
  • 1
    So I just came across a bug. If the player turns their head the arm that is being mirrored will move with the head. Currently I am using the entire player model as the playerTransform. Is there something else I should use? I will edit my original post with a screen shot of my Unity. – Afroca Jul 15 '19 at 18:17
  • 1
    Ah, I thought the oculus view was only turning the head or something. You could go into `ReflectRelativeVector` and replace both occurrences of `playerTransform.right` with `Vector3.right` and it would reflect the hands as if the player's body is always unrotated. – Ruzihm Jul 15 '19 at 18:32
  • 1
    Actually don't edit yours. I left an explanation in my answer but I am leaving yours as the correct answer. – Afroca Jul 15 '19 at 19:01
  • 1
    I have posted a new question. Would you be able to take a look at it if you may know the answer? – Afroca Jul 19 '19 at 14:12
1

I made some alteration to what @Ruzihm had. Thank you so much for the help. Everything works perfectly in the code I sampled below but I would recommend @Ruzihm answer since how he handles rotations. This code works if the player model is stationary and you are not turning your full body. If you need to turn use: playerTransform.right instead of Vector3.right in the ReflectRelativeVector function but using playerTransform.right will move the arm as the head moves.

public class VRMirror : MonoBehaviour
{
    public bool mirrorLeft;
    public bool mirrorRight;
    public GameObject leftHand; //left hand anchor in OVRCameraRig
    public GameObject rightHand; //right hand anchor from OVRCameraRig

    public Transform playerTransform;  

    void Start()
    {

    }
    void FixedUpdate()
    {

        Transform left = leftHand.GetComponent<Transform>();
        Transform right = rightHand.GetComponent<Transform>();
        if (mirrorLeft)
        {
             MirrorFromTo(left, right);
        }
        else if (mirrorRight)
        {
            MirrorFromTo(right, left);

        }
    }

    void MirrorFromTo(Transform sourceTransform, Transform destTransform)
    {
        // Determine dest position
        Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
        Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
        destTransform.position = playerTransform.position + playerToDestHand;

        // Determine dest rotation
        Quaternion destRot = sourceTransform.rotation;

        destRot.y = -destRot.y;
        destRot.z = -destRot.z;
        destTransform.rotation = destRot;

    }

    Vector3 ReflectRelativeVector(Vector3 relativeVec)
    {
        // relativeVec
        //     Take the relative vector....
        // + Vector3.Dot(relativeVec, playerTransform.right)
        //     and for how far along the player's right direction it is 
        //     away from the player (may be negative),
        // * playerTransform.right
        //     move it that distance along the player's right...
        // * -2f
        //    negative two times (i.e., along the left direction 2x)

        return relativeVec
        + Vector3.Dot(relativeVec, Vector3.right)
            * Vector3.right
            * -2f;
    }
}

Here is a screen shot of the editor: enter image description here

Afroca
  • 115
  • 11
  • 1
    I think this is the first time I've seen anyone on stack overflow modify [the components](https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation) of a quaternion and successfully get what they want. Impressive! – Ruzihm Jul 15 '19 at 16:41
  • 1
    @Ruzihm Logically I am doing exactly what you're doing in regards to the rotations. I am still very new to Unity though. Thank you again for the help! I definitely will try to reach out to you in the future for help! – Afroca Jul 15 '19 at 17:19