View Full Version : Using skinCluster->setWeights() faster
05 May 2009, 03:37 AM
I have an importer/exporter plugin I am building. After a steep learning curve, I have a significant speed issue in the importer code when trying to set the skin weights to follow the original model.
First, I could only figure out how to create a skinCluster by executing a mel command "skinCluster -n \"skinCluster1\" -mi 4 " (plus the mesh name appended). This works, reasonably well, but the computed weights are not as polished as what I am trying to import.
I used the same method I export with to walk through the components, getting the skin weights with skinCluster->getweights(). This works well in the exporter. Then I took the weights retreived in the MDoubleArray, zeroed them out and wrote the (up to) four values into the MDoubleArray, then executed a skinCluster->setWeights().
Alas, this is very slow, maybe by a hundred times slower. I suspect that Maya is updating the entire scene on each setWeights() call. How can I freeze this until I have all of the values set?
Or maybe there is an even better method of creating or updating the weights someone can share?
05 May 2009, 03:00 PM
Are you normalizing the weights on other influences as they are applied? Assuming that the data from your exporter is already normalized, perhaps it's best to set the normalize parameter to false in your setWeights method. I know this has cause performance issues in the past.
Also, just to be sure, you're using the batch setWeights call right? That is, you only need to call setWeights once to set it for all components and all influences to those components, rather than a setWeights call per component. I'd imagine this would speed things up as well.
MStatus (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_status.html) MFnSkinCluster::setWeights(const MDagPath (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_dag_path.html) & path,
const MObject (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_object.html) & components,
MIntArray (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_int_array.html) & influenceIndices,
MDoubleArray (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_double_array.html) & values,
bool normalize = true,
MDoubleArray (http://torw3.autodesk.com/maya/TechDocBuilt/main/externalDoc/API/class_m_double_array.html) * oldValues = NULL )
05 May 2009, 05:28 PM
Thanks. It is calling setWeights() per component, I will revise it to follow your suggestion.
<* Wes *>
05 May 2009, 01:04 PM
yup this is a known issue. There is however a much quicker way to set the data, and that's to access the weight plugs directly.
05 May 2009, 02:50 PM
I obtained some minor (2x) improvements by reordering my function calls. Still slow (takes longer to do this than it does to load Maya). But the batch call suggestion looks like I would be making perhaps 20 or 30 megs of data buffers: numBones*max_influences*numVertices*sizeof(double)
That cannot be fast to create and parse, either.
I'm at the limits of my competency there. I haven't done any plugs for anything yet. If you could do a little thumbnail of the core steps (where to obtain one from, how to poke it) I would appreciate it. I will then merrily research the API docs for the nitty-gritty details.
<* Wes *>
05 May 2009, 03:56 PM
To access the skinCluster weight directly, here is the layout:
n = numVertices
m = numBones
So the first index, n, indexes the particular vertex component that you want to set the weight for. The second index, m, indexes the influence weight you want to set for that vertex.
The order of the influence weights is based on the order of joints connected into the:
Each joint is connected into this matrix attribute.
To access the skinCluster data in the code, you could access it in this fashion, inside an MPxCommand. Note, this is untested code, but it should give you a general idea of which classes to look at.
// [in] skinCluster - MObject of skinCluster node you want to modify
MFnDependencyNode depFn( skinCluster );
MPlug pWeightList = depFn.findPlug( "weightList" );
int nElem = pWeightList.numElements();
for( int i = 0; i < nElem; i++ )
MPlug pWeightListElem = pWeightList.elementByLogicalIndex( i );
// An array of arrays attribute is essentially an array of compound attributes,
// each consisting of a single child. So, after obtaining the element plug for pWeightList,
// we must call the child() method to grab the .weights() array attribute.
MPlug pWeight = pWeightListElem.child(0);
int nWeightElem = pWeight.numElements();
for( int j = 0; j < nWeightElem; j++ )
// The actual array attribute doesn't have data, only the elements, so grab
// the element then set the value.
MPlug pWeightElement = pWeight.elementByLogicalIndex( j );
// Set the appropriate value here for index [i,j], i = vertex, j = influence
pWeightElement.setValue( 0.0 );
05 May 2009, 05:41 PM
Very helpful. I appreciate it... I wasn't looking for tested code, and the example is more complete than anything I expected. I'll post back with my results.
<* Wes *>
05 May 2009, 06:42 PM
In my experience the quickest wa to set/get the weights is using functions that use the weights of all influences in a MDoubleArray. Works even on complex meshes in a reasonable amount of time and can easily be manipulated like for mirroring weights etc. But get/set only once, that is important.
05 May 2009, 06:42 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.