NVIDIAGameWorks / FleX

Other
650 stars 100 forks source link

How to rotate a shape in FleX so that the rotation of the object matches with the same rotation in Blender? #64

Closed Amir-Arsalan closed 4 years ago

Amir-Arsalan commented 5 years ago

I import a mesh in NVIDIA's FleX, rotate it and store it on disk; I call this mesh X. I also import the mesh in Blender and rotate it the same amount; I call this mesh Y. After I imported X in Blender I realized that X has a different rotation which means that FleX is either using a different global axes (I think it's -Y forward and Z up) or it applies the rotations in a different order or a combination of both. So I am pretty confused now. I do not want to change anything on the FleX side. However, I want to rotate Y in a way that it matches with what FleX exports (X) after rotation. I also tried with Quaternions but I'm still unable to figure out the way FleX is transforming the object. The only thing that I've been able to figure out empirically is that the value for rx rotation is applied the same way for both FleX and Blender. This means that objects X and Y would overlap completely if imported in on scene in Blender.

My goal is to rotate the object in FleX so that object Y's rotation matches exactly with object X when I import it in Blender. How can I achieve this?

Note that I am not changing the rotation order in Blender and I'm using the default XYZ order for rotation.

Here I are some images of the visual differences between FleX and Blender for such transformations:

Euler rotation vector: [0, 89.9, 0] in for object X in Blender. After applying the rotation in Blender (object X):

1

After applying the same rotation ([0, 89.9, 0]) in FleX and importing the object Y in Blender:

1

Euler rotation vector: [0, 0, 89.9] for object X in Blender:

1

After applying the same rotation ([0, 0, 89.9]) in FleX and importing the object Y in Blender:

1

It might look it is easy to guess from the images that FleX swaps the ry and rz rotation to do the rotation. If I also swap the rotations in Blender then both X and Y totally overlap with each other. However, if the rotation vector is [-43.964176, 20.641195, -1.2689421] X and Y do not overlap anymore and discrepancies start to show up:

1

The discrepancies are more visible if I put a cloth on Y in FleX, import the cloth and X in Blender and rotate X by [-43.964176, -1.2689421, 20.641195](notice that I've swapped ry and rz:

1

Note that if I import Y here, the cloth would be perfectly covering it while touching it on the surface:

1

As you probably know FleX computes and uses its rotation matrices as follow. The top one is used when Euler angles are used and the second one is used when Quaternions are input:

// generate a rotation matrix around an axis, from PBRT p74
inline Mat44 RotationMatrix(float angle, const Vec3& axis)
{
    Vec3 a = Normalize(axis);
    float s = sinf(angle);
    float c = cosf(angle);

    float m[4][4];

    m[0][0] = a.x * a.x + (1.0f - a.x * a.x) * c;
    m[0][1] = a.x * a.y * (1.0f - c) + a.z * s;
    m[0][2] = a.x * a.z * (1.0f - c) - a.y * s;
    m[0][3] = 0.0f;

    m[1][0] = a.x * a.y * (1.0f - c) - a.z * s;
    m[1][1] = a.y * a.y + (1.0f - a.y * a.y) * c;
    m[1][2] = a.y * a.z * (1.0f - c) + a.x * s;
    m[1][3] = 0.0f;

    m[2][0] = a.x * a.z * (1.0f - c) + a.y * s;
    m[2][1] = a.y * a.z * (1.0f - c) - a.x * s;
    m[2][2] = a.z * a.z + (1.0f - a.z * a.z) * c;
    m[2][3] = 0.0f;

    m[3][0] = 0.0f;
    m[3][1] = 0.0f;
    m[3][2] = 0.0f;
    m[3][3] = 1.0f;

    return Mat44(&m[0][0]);
}

inline Mat44 RotationMatrix(Quat q)
{
    Matrix33 rotation(q);

    Matrix44 m;
    m.SetAxis(0, rotation.cols[0]);
    m.SetAxis(1, rotation.cols[1]);
    m.SetAxis(2, rotation.cols[2]);
    m.SetTranslation(Point3(0.0f));

    return m;
}

And here's how I apply the Euler rotation vector:

obj->Transform(RotationMatrix(rx, Vec3(1.0f, 0.0f, 0.0f)));
obj->Transform(RotationMatrix(ry, Vec3(0.0f, 1.0f, 0.0f)));
obj->Transform(RotationMatrix(rz, Vec3(0.0f, 0.0f, 1.0f)));

And I think this should be how Transform() is defined:

void Mesh::Transform(const Matrix44& m)
{
    for (uint32_t i=0; i < m_positions.size(); ++i)
    {
        m_positions[i] = m*m_positions[i];
        m_normals[i] = m*m_normals[i];
    }
}
Amir-Arsalan commented 5 years ago

I just had to make the following changes to [manually] change the coordinate system of FleX to the one that Blender uses:

obj->Transform(RotationMatrix(op.rotate.x, Vec3(1.0f, 0.0f, 0.0f)));
obj->Transform(RotationMatrix(op.rotate.y, Vec3(0.0f, 0.0f, -1.0f)))
obj->Transform(RotationMatrix(op.rotate.z, Vec3(0.0f, 1.0f, 0.0f)));

The way I figured this was I generated three rotation vectors [90, 0, 0], [0, 90, 0] and [0, 0, 90] and rotated the shapes in FleX. Then I loaded the shapes in Blender and empirically figured rotation on which axis in Blender matches to the shape imported from FleX. I learned that when I rotate the shape on the Y axis by 90 degrees in Blender, it matches the 90-degree rotation on the Z axis in FleX. Also, I learned that rotating the object by -90 degrees on the Z axis in Blender matches with the 90-degree rotation on the Y axis in FleX.

@mmacklin Now I have a similar issue with Quaternions. I know how to change the rotation matrix for Quaternions but I don't immediately see which parts of quat.h I should change to do this in practice. Things look a bit cryptic in quat.h for me. Could you please guide me on this?

qi55wyqu commented 4 years ago

I just had to make the following changes to [manually] change the coordinate system of FleX to the one that Blender uses:

obj->Transform(RotationMatrix(op.rotate.x, Vec3(1.0f, 0.0f, 0.0f)));
obj->Transform(RotationMatrix(op.rotate.y, Vec3(0.0f, 0.0f, -1.0f)))
obj->Transform(RotationMatrix(op.rotate.z, Vec3(0.0f, 1.0f, 0.0f)));

The way I figured this was I generated three rotation vectors [90, 0, 0], [0, 90, 0] and [0, 0, 90] and rotated the shapes in FleX. Then I loaded the shapes in Blender and empirically figured rotation on which axis in Blender matches to the shape imported from FleX. I learned that when I rotate the shape on the Y axis by 90 degrees in Blender, it matches the 90-degree rotation on the Z axis in FleX. Also, I learned that rotating the object by -90 degrees on the Z axis in Blender matches with the 90-degree rotation on the Y axis in FleX.

@mmacklin Now I have a similar issue with Quaternions. I know how to change the rotation matrix for Quaternions but I don't immediately see which parts of quat.h I should change to do this in practice. Things look a bit cryptic in quat.h for me. Could you please guide me on this?

Take a look at this project, which uses Flex for haptic rendering: FleX-Chai3d

It includes helper functions to convert from the Flex coordinate system to the one that Chai3D uses, which is the standard x,y,z. This might help to convert the quaternions as well.

Vec3 FromChai(const cVector3d& a_vector) {
    return Vec3(a_vector.y(), a_vector.z(), a_vector.x());
}

cVector3d ToChai(const Vec3& a_vector) {
    return cVector3d(a_vector.z, a_vector.x, a_vector.y);
}

cQuaternion ToChai(const Quat& a_quat) {
    return cQuaternion(a_quat.w, a_quat.z, a_quat.x, a_quat.y);
}

cMatrix3d ToChaiRotMat(const Quat& a_quat) {
    cMatrix3d rotMat;
    ToChai(a_quat).toRotMat(rotMat);
    return rotMat;
}