Announcements

Between mid-October and November, the content on AREA will be relocated to the Autodesk Community M&E Hub and the Autodesk Community Gallery. Learn more HERE.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Fix MPxSkinCluster C++ code sample in Maya documentation

Fix MPxSkinCluster C++ code sample in Maya documentation

 

In the MPxSkinCluster code sample the code that demonstrate standard skinning implementation is wrong.  Overall there are issues due to a misuse of physical and logical indices of the matrix and skin weights attributes.

 

First the code sample assumes that influence transforms (usually joints) are always connected to the attribute MPxSkinCluster::matrix in a consecutive manner which is wrong since you can have:

 

 

 

skin_cluster.matrix:  joint1, joint2, joint3
logical  index:            1,      3,      4
physical index:            0,      1,      2

 

 

 

Similarly it is assumed that the logical index 'j' of "weightList[i].weight[j]" is the same as the physical index, which leads to bugs when joints are not connected in a consecutive manner or if "weightList[i].weight[j]" is sparse.

 

Here is the faulty version with some annotations:

 

 

 

for ( ; !iter.isDone(); iter.next()) {
    MPoint pt = iter.position();
    MPoint skinned;
    MArrayDataHandle weightsHandle = weightListHandle.inputValue().child( weights );
    // wrong the size "numTransforms" can change from vertex to vertex
    // and should be fetched dynamicaly with weightsHandle.elementCount();
    for ( int i=0; i<numTransforms; ++i ) {
        // 'i' here is the physical index of the transform
        // jumpToElement() should be used with a logical index!
        if ( MS::kSuccess == weightsHandle.jumpToElement( i ) ) {
            skinned += ( pt * transforms[i] ) * weightsHandle.inputValue().asDouble();
        }
    }
    iter.setPosition( skinned );
    weightListHandle.next();
}

 

 

 

The correct code tested on various models and Maya 2020:

 

 

 

MStatus MyCustomSkinCluster::deform(MDataBlock& block,
                                    MItGeometry& iter,
                                    const MMatrix& worldMat,
                                    unsigned int /*multiIndex*/)
{
    // get the influence transforms
    MArrayDataHandle transformsHandle = block.inputArrayValue(matrix);
    int nb_joints = transformsHandle.elementCount();
    if (nb_joints == 0) {
        return MS::kSuccess;
    }

    MArrayDataHandle bindHandle = block.inputArrayValue(bindPreMatrix);

    // LBS
    MArrayDataHandle weightListHandle = block.inputArrayValue( weightList );
    if ( weightListHandle.elementCount() == 0 ) {
        // no weights - nothing to do
        return MS::kSuccess;
    }
    const MMatrix worldMatInverse = worldMat.inverse();
    for ( iter.reset(); !iter.isDone(); iter.next())
    {
        // In this fix we also support meshes associated to a transform other 
        // than identity:
        MPoint pt = iter.position() * worldMat;
        MPoint skinned;
        // get the weights for this point
        MArrayDataHandle weightsHandle = weightListHandle.inputValue().child( weights );
        int nb_weights = weightsHandle.elementCount();
        for (int i = 0; i < nb_weights; i++)
        {
            weightsHandle.jumpToArrayElement(i);
            double w = weightsHandle.inputValue().asDouble();

            // logical index represent the actuall joint index
            int joint_idx = weightsHandle.elementIndex();

            transformsHandle.jumpToElement( joint_idx ); // Jump to logical index
            MMatrix mat = MFnMatrixData(transformsHandle.inputValue().data()).matrix();

            bindHandle.jumpToElement( joint_idx ); // Jump to logical index
            MMatrix preBindMatrix = MFnMatrixData( bindHandle.inputValue().data() ).matrix();
            mat = preBindMatrix * mat;

            skinned += ( pt * mat ) * w;
        }
        // Set the final position.
        iter.setPosition( skinned * skinnedWorldMatInverse );
        // advance the weight list handle
        weightListHandle.next();
    }
    return MS::kSuccess;
}

 

 

 

 

 

 

 

@tj.galda 

Can't find what you're looking for? Ask the community or share your knowledge.

Submit Idea