Search code examples
c#unity-game-enginerotationquaternions

Capture position and rotation of an object relative to another object


I have two identical objects and two cameras. What I'd like to achieve is the following:

1) Capture the position and rotation of Cube A relative to Camera A

2) Transfer that to a Cube B, so that in Camera B (which I cannot move), Cube B looks exactly as Cube A looks in Camera A

I was successful doing that with position with the following code:

positionDifference = CubeA.InverseTransformPoint(CameraA.transform.position);

To transfer it onto Cube B, I do:

cubeBpos = transform.InverseTransformPoint(CubeB.transform.localPosition);      

while ( Mathf.Abs (cubeBpos.x + positionDifference.x) > 0.01f ) {
    if (cubeBpos.x + positionDifference.x > 0) {
        CubeB.transform.position += new Vector3(-0.01f, 0, 0);
    } 
    else if (cubeBpos.x + positionDifference.x < 0) {
        CubeB.transform.position += new Vector3(+0.01f, 0, 0);  
    }
    cubeBpos = transform.InverseTransformPoint(CubeB.transform.position);
}

That's clunky, but works. However, when I try to transfer rotations, Cube B starts to pivot around the origin. Interestingly, when I move Cube A in world coordinates, Cube B moves in local, and vice versa. I suspect local-to-world coordinate translation is an issue, but I also think my rotation code is naive. I tried to capture rotation in two ways, first like this:

rotationDifference = Quaternion.Inverse(CubeA.transform.rotation) * CameraA.transform.rotation;

CubeB.transform.rotation = Quaternion.Inverse(rotationDifference);

Second attempt:

rotationDifference = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);

CubeB.transform.eulerAngles = rotationDifference;

Both approaches resulted in weird rotational offsets. I tried using localPosition and localEulerAngles, didn't help.

I'm hoping there's a smarter way to do this :)

EDIT: Here's a Dropbox link to the project


Solution

  • The problem is that you are treating position and rotation separately although they influence each other. Let's put both together and say we have model transforms for the two cameras and the two cubes (represented as homogeneous matrices; assuming column vectors). Then, we want to find the transform for cube B TCubeB, such that:

    TCameraA^-1 * TCubeA = TCameraB^-1 * TCubeB
    

    Note that TCamera is the model transform of the camera, not the view matrix. If you have the view matrix, simply leave the inverse away.

    We can immediately solve for TCubeB:

    TCameraB * TCameraA^-1 * TCubeA = TCubeB
    

    I'm not too familiar with the Unity API but it seems like you cannot use transformation matrices directly. So let's split the transformation matrix T in a rotational part R and a translational part Tr:

    TrCameraB * RCameraB * (TrCameraA  * RCameraA)^-1 * TrCubeA * RCubeA = TrCubeB * RCubeB
    TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA = TrCubeB * RCubeB
    

    If we care only about the rotation, we can calculate the respective quaternion by simply doing:

    QCameraB * QCameraA^-1 * QCubeA = QCubeB
    

    The translation becomes a bit more difficult. We need to find the translation transform, such that

    TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA * RCubeB^-1 = TrCubeB
    

    To find the translation vector, simply multiply the origin to the left-hand side:

    TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA * RCubeB^-1 * (0, 0, 0, 1)
    

    In pseudo-code, this boils down to (the matrices that appear stand for the respective translation vectors):

    Vector4 translation = (0, 0, 0, 1)
    translation += TrCubeA
    translation -= TrCameraA
    translation = RCameraA.Inverse().Transform(translation)
    translation = RCameraB.Transform(translation)
    translation += TrCameraB
    

    Again, I barely know the Unity API and it might use some different conventions than I did (conventions in transformation math are especially tricky). But I am sure you can adapt the above derivations if something is not correct.