1

I have a human bipedal animation file format that I would like to programmatically read into Maya using the C++ API.

The animation file format is similar to that of the Open Asset Importer's per-node animation structure.

For every joint, there is a series of up to 60 3D vector keys (to describe the translation of the joint) and 60 quaternion keys (to describe the rotation of the joint). Every joint is guaranteed to have the same number of keys (or no keys at all).

The length (time in seconds) of the animation can be specified or changed (so that you can set the 60 keys to happen over 2 seconds for a 30 FPS animation, for example).

The translations and rotations of the joints propagates down the skeleton tree every frame, producing the animation.

Here's a sample. Additional remarks about the data structure are added by the logging facility. I have truncated the keys for brevity.

Bone Bip01
    Parent null
    60 Position Keys
    0 0.000000 4.903561 99.240829 -0.000000
    1 0.033333 4.541568 99.346550 -2.809127
    2 0.066667 4.182590 99.490318 -5.616183
    ... (truncated)
    57 1.366667 5.049816 99.042770 -116.122604
    58 1.400000 4.902135 99.241692 -118.754120
    59 1.400000 4.902135 99.241692 -118.754120

    60 Rotation Keys
    0 0.000000 -0.045869 0.777062 0.063631 0.624470
    1 0.033333 -0.043855 0.775018 0.061495 0.627400
    2 0.066667 -0.038545 0.769311 0.055818 0.635212
    ... (truncated)
    57 1.366667 -0.048372 0.777612 0.065493 0.623402
    58 1.400000 -0.045869 0.777062 0.063631 0.624470
    59 1.400000 -0.045869 0.777062 0.063631 0.624470

Bone Bip01_Spine
    Parent Bip01
    60 Position Keys
    ...
    60 Rotation Keys
    ...

In C++, the data structure I currently have corresponds to this:

std::unordered_map<string, std::vector<Vector3>> TranslationKeyTrack is used to map a set of translation vectors to the corresponding bone.

std::unordered_map<string, std::vector<Quaternion>> RotationKeyTrack is used to map a set of rotation quaternions to the corresponding bone.

Additional notes: There are some bones that do not move relative to its parent bone; these bones have no keys at all (but has an entry with 0 keys). There are also some bones that have only rotation, or only position keys. The skeleton data is stored in a separate file that I can already read into Maya using MFnIkJoint.

The bones specified in the animation file is 1:1 to the bones in that skeleton data.

Now I would like to import this animation data into Maya. However, I do not understand Maya's way of accepting animation data through its C++ API.

In particular, the MFnAnimCurve function set addKeyFrame or addKey accepts only a single floating point value tied to a time key, while I have a list of vectors and quaternions. MFnAnimCurve also accepts 'tangents'; after reading the documentation, I am still unsure of how to convert the data I have into these tangents.

My question is: How do I convert the data I have into something Maya understands?

I understand better with examples, so some sample code will be helpful.

4

1 回答 1

1

因此,经过几天的反复试验和检查互联网上的少数代码片段后,我设法想出了一些可行的方法。

鉴于上述TranslationKeyTrackRotationKeyTrack,

遍历骨架。对于每个关节,

  1. 设置骨架的初始位置和方向。这是必需的,因为有些关节相对于其父关节不会移动;如果未设置初始位置和方向,则整个骨架可能会不规则地移动。
  2. 设置 AnimCurve 键。

迭代看起来像这样:

MStatus status;
MItDag dagIter(MItDag::kDepthFirst, MFn::kJoint, &status);
    for (; !dagIter.isDone(); dagIter.next()) {
        MDagPath dagPath;
        status = dagIter.getPath(dagPath);
        MFnIkJoint joint(dagPath);
        string name_key = joint.name().asChar();

        // Set initial position, and the translation AnimCurve keys.
        if (TranslationKeyTrack.find(name_key) != TranslationKeyTrack.end()) {
            auto pos = TranslationKeyTrack[name_key][0];
            joint.setTranslation(MVector(pos.x, pos.y, pos.z), MSpace::kTransform);
            setPositionAnimKeys(dagPath.node(), positionTracks[name_key]);
        }

        // Set initial orientation, and the rotation AnimCurve keys.
        if (RotationKeyTrack.find(name_key) != RotationKeyTrack.end()) {
            auto rot = rotationTracks[name_key][0];
            joint.setOrientation(rot.x, rot.y, rot.z, rot.w);
            setRotationAnimKeys(dagPath.node(), RotationKeyTrack[name_key]);
        }
}

为简洁起见,我将省略显示setPositionAnimKeyssetRotationAnimKeys仅显示。但是,两者的想法是相同的。请注意,我用于kAnimCurveTL翻译轨道。

void MayaImporter::setRotationAnimKeys(MObject joint, const vector<Quaternion>& rotationTrack) {
    if (rotationTrack.size() < 2) return; // Check for empty tracks.

    MFnAnimCurve rotX, rotY, rotZ;
    setAnimCurve(joint, "rotateX", rotX, MFnAnimCurve::kAnimCurveTA);
    setAnimCurve(joint, "rotateY", rotY, MFnAnimCurve::kAnimCurveTA);
    setAnimCurve(joint, "rotateZ", rotZ, MFnAnimCurve::kAnimCurveTA);

    MFnIkJoint j(joint);
    string name = j.name().asChar();

    for (int i = 0; i < rotationTrack.size(); i++) {
        auto rot = rotationTrack[i];
        MQuaternion rotation(rot.x, rot.y, rot.z, rot.w);

        // Depending on your input, you may have to do additional processing
        // to get the correct Euler rotation here.
        auto euler = rotation.asEulerRotation();
        MTime time(FPS*i, MTime::kSeconds); // FPS is a number defined elsewhere.

        rotX.addKeyframe(time, euler.x);
        rotY.addKeyframe(time, euler.y);
        rotZ.addKeyframe(time, euler.z);
    }
}

最后,我用于setAnimCurve. 它本质上将 AnimCurve 附加到关节。这段代码改编自这里的 mocap 文件导入器。万岁开源!

void MayaImporter::setAnimCurve(const MObject& joint, const MString attr, MFnAnimCurve& curve, MFnAnimCurve::AnimCurveType type) {
    MStatus status;
    MPlug plug = MFnDependencyNode(joint).findPlug(attr, false, &status);

    if (!plug.isKeyable())
        plug.setKeyable(true);

    if (plug.isLocked())
        plug.setLocked(false);

    if (!plug.isConnected()) {
        curve.create(joint, plug, type, nullptr, &status);
        if (status != MStatus::kSuccess)
            cout << "Creating anim curve at joint failed!" << endl;
    } else {
        MFnAnimCurve animCurve(plug, &status);
        if (status == MStatus::kNotImplemented)
            cout << "Joint " << animCurve.name() << " has more than one anim curve." << endl;
        else if (status != MStatus::kSuccess)
            cout << "No anim curves found at joint " << animCurve.name() << endl;
        curve.setObject(animCurve.object(&status));
    }
}
于 2016-05-05T15:52:47.250 回答