0
\$\begingroup\$

I'm currently working with Oculus integration trying to export headset (and eventually controller) position and rotation data to a CSV for later use in Blender. My issue comes from the way Unity (or Blender) handles rotations. In Unity, 0 = 360, whereas in Blender 0 != 360.

My current solution is to grab the current rotation and subtract it from the rotation in the frame before. I then combine the difference with a running sum (Vector3 for x, y, and z sums). If any sum is greater than 360, I add a rotation to a separate Vector3 that tracks the number of complete rotations for each axis. I subtract rotations for sums less than -360. This way I can calculate the final rotation (x for example) using 360*numRots.x + xrot to get the overall rotation.

Unfortunately my code doesn't properly store the running total correctly; it always matches the current rotation. For example, if I run the program, the current rotation might be (270, 23, -9) and the running total SHOULD be (0, 0, 0) (plus or minus some degrees) but the running total always matches the current rotation.

Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using System;

public class Mocap : MonoBehaviour
{

    public GameObject cam;
    static string path = "Assets/test.txt";
    StreamWriter writer;
    private Vector3 rotationSum; // running sums of x, y, z rotations
    private Vector3 numRots; // total number of rotations per axis
    private Vector3 prevRot; // previous rotation values

    void Start()
    {
        writer = new StreamWriter(path, append: false);
        rotationSum = new Vector3(0, 0, 0);
        numRots = new Vector3(0, 0, 0);
    }

    void Update()
    {
        // get camera pos and rot
        double xpos = cam.transform.position.x;
        double ypos = cam.transform.position.y;
        double zpos = cam.transform.position.z;
        float xrot = cam.transform.rotation.eulerAngles.x;
        float yrot = cam.transform.rotation.eulerAngles.y;
        float zrot = cam.transform.rotation.eulerAngles.z;
        Vector3 curr = new Vector3(xrot, yrot, zrot);

        Vector3 tempDiff = curr - prevRot;
        rotationSum += tempDiff;

        // check x
        if(rotationSum.x >= 360)
        {
            rotationSum.x -= 360;
            numRots.x += 1;
        } else if(rotationSum.x <= -360)
        {
            rotationSum.x += 360;
            numRots.x -= 1;
        }

        // check y
        if (rotationSum.y >= 360)
        {
            rotationSum.y -= 360;
            numRots.y += 1;
        }
        else if (rotationSum.y <= -360)
        {
            rotationSum.y += 360;
            numRots.y -= 1;
        }

        // check z
        if (rotationSum.z >= 360)
        {
            rotationSum.z -= 360;
            numRots.z += 1;
        }
        else if (rotationSum.z <= -360)
        {
            rotationSum.z += 360;
            numRots.z -= 1;
        }

        // final rotation
        xrot = 360 * numRots.x + xrot;
        yrot = 360 * numRots.y + yrot;
        zrot = 360 * numRots.z + zrot;

        // update previous values
        prevRot = curr;

        string filetext = xpos + ", " + ypos + ", " + zpos + ", " + xrot + ", " + yrot + ", " + zrot;

        this.writer.WriteLine(filetext);

    }
}

EDIT I have converted the code to use Quaternions I believe. I display in Euler angles for convenience. The next problem I'm facing is when the calculating the difference between the current and previous rotation and when the rotation jumps from 0-360 or 360-0. If curr=5 and prev=355, the difference is -350 which correctly gets added to the running sum but this still acts as if I rotated through 355 down to 5 rather than looping at 360 to 0 then to 5. My current thinking to fix this is check if the difference is more than say 300. If so, that mean I either a) rotated my head 300 degrees in 1/24th second or b) I looped and should add a rotation to the axis. Advice is appreciated.

Also the reason I have to wait is because the headset rotation is set to (0,0,0) until it loads so I just wait until it doesn't equal (0,0,0).

Code:

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

public class mocapv2 : MonoBehaviour
{
    // misc objects
    public GameObject cam;
    public Text uitext;
    private bool onecheck;
    // quaternion stuff
    private Quaternion previous, sum;
    void Start()
    {
        previous = sum = Quaternion.identity;
        this.onecheck = true;
    }

    private IEnumerator waitasec()
    {
        yield return new WaitForSeconds(.1f);
    }

    void Update()
    {
        // wait for updated headset rotation
        if(this.onecheck)
        {
            this.previous = cam.transform.rotation;
            if(this.previous == Quaternion.identity)
            {
                StartCoroutine(waitasec());
            } else
            {
                this.onecheck = false;
            }
        }

        if(!this.onecheck)
        {
            Quaternion curr = cam.transform.rotation;
            Quaternion difference = curr * Quaternion.Inverse(this.previous);
            this.sum *= difference;
            string text = "curr:" + curr.eulerAngles + "\ndiff:" + difference.eulerAngles + "\nprev:" + this.previous.eulerAngles + "\nsum:" + this.sum.eulerAngles;
            uitext.text = text;
            this.previous = curr;
        }
    }
}
```
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Adding up incremental Euler angle rotations is generally not safe. Euler angles are simple to write down, but miserable to do math with. Have you considered using Quaternions or basis vectors instead, which are much more consistent across their domain? \$\endgroup\$
    – DMGregory
    Jan 25 '21 at 5:31
  • 1
    \$\begingroup\$ @DMGregory that's what I've recently been looking into. The only reason I started with Euler angles was because it was much easier for me to understand/visualize what was happening and when a full rotation along an axis occurred. From the reading I've been doing, I think I'll just have to suck it up and learn Quaternions. \$\endgroup\$
    – Josh Brown
    Jan 25 '21 at 14:37
0
\$\begingroup\$

Holding onto a running total isn't going to work because floating-point doesn't have the level of accuracy to maintain an exact value over time. If you can output quaternions and work with that in Blender that would be ideal. If not extract the Euler angles from the current quaternion value: camera.transform.rotation.euler.

\$\endgroup\$
1
  • \$\begingroup\$ The problem isn't necessarily the format of the output, I can use quaternions or eulers in either though I'm aware that quaternions are better. The problem is in Unity, rotation is clamped where 360=0, but in Blender, rotation isn't clamped so 360!=0. In Blender, an object rotated 360 degrees is different than an object rotated 720 degrees even though they appear the same. I'm working with keyframes so I either need to be able to count rotations done in Unity or somehow tell Blender that 360=0. \$\endgroup\$
    – Josh Brown
    Jan 25 '21 at 18:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .