MStatus sgBulgeDeformer::deform(MDataBlock& dataBlock, MItGeometry& iter, const MMatrix& mtx, unsigned int index) { MStatus status; float bulgeWeight = dataBlock.inputValue(aBulgeWeight).asFloat(); double bulgeRadius = dataBlock.inputValue(aBulgeRadius).asDouble(); MArrayDataHandle hArrInputs = dataBlock.inputArrayValue(aBulgeInputs); MPointArray allPositions; iter.allPositions(allPositions); if (mem_resetElements) { unsigned int elementCount = hArrInputs.elementCount(); mem_meshInfosInner.resize(mem_maxLogicalIndex); mem_meshInfosOuter.resize(mem_maxLogicalIndex); for (unsigned int i = 0; i < elementCount; i++, hArrInputs.next()) { MDataHandle hInput = hArrInputs.inputValue(); MDataHandle hMatrix = hInput.child(aMatrix); MDataHandle hMesh = hInput.child(aMesh); MMatrix mtxMesh = hMatrix.asMatrix(); MObject oMesh = hMesh.asMesh(); MFnMeshData meshDataInner, meshDataOuter; MObject oMeshInner = meshDataInner.create(); MObject oMeshOuter = meshDataOuter.create(); MFnMesh fnMesh; fnMesh.copy(oMesh, oMeshInner); fnMesh.copy(oMesh, oMeshOuter); sgMeshInfo* newMeshInfoInner = new sgMeshInfo(oMeshInner, hMatrix.asMatrix()); sgMeshInfo* newMeshInfoOuter = new sgMeshInfo(oMeshOuter, hMatrix.asMatrix()); mem_meshInfosInner[hArrInputs.elementIndex()] = newMeshInfoInner; mem_meshInfosOuter[hArrInputs.elementIndex()] = newMeshInfoOuter; } } for (unsigned int i = 0; i < elementCount; i++) { mem_meshInfosInner[i]->setBulge(bulgeWeight, MSpace::kWorld ); } MFloatArray weightList; weightList.setLength(allPositions.length()); for (unsigned int i = 0; i < weightList.length(); i++) weightList[i] = 0.0f; MMatrixArray inputMeshMatrixInverses; inputMeshMatrixInverses.setLength(elementCount); for (unsigned int i = 0; i < elementCount; i++) { inputMeshMatrixInverses[i] = mem_meshInfosInner[i]->matrix(); } for (unsigned int i = 0; i < allPositions.length(); i++) { float resultWeight = 0; for (unsigned int infoIndex = 0; infoIndex < elementCount; infoIndex++) { MPoint localPoint = allPositions[i] * mtx* inputMeshMatrixInverses[infoIndex]; MPoint innerPoint = mem_meshInfosInner[infoIndex]->getClosestPoint(localPoint); MPoint outerPoint = mem_meshInfosOuter[infoIndex]->getClosestPoint(localPoint); MVector innerVector = innerPoint - localPoint; MVector outerVector = outerPoint - localPoint; if (innerVector * outerVector < 0) { double innerLength = innerVector.length(); double outerLength = outerVector.length(); double allLength = innerLength + outerLength; float numerator = float( innerLength * outerLength ); float denominator = float( pow(allLength / 2.0, 2) ); resultWeight = numerator / denominator; } } weightList[i] = resultWeight; } for (unsigned int i = 0; i < allPositions.length(); i++) { allPositions[i] += weightList[i] * MVector(0, 1, 0); } iter.setAllPositions(allPositions); return MS::kSuccess; }
bool ToMayaSkinClusterConverter::doConversion( IECore::ConstObjectPtr from, MObject &to, IECore::ConstCompoundObjectPtr operands ) const { MStatus s; IECore::ConstSmoothSkinningDataPtr skinningData = IECore::runTimeCast<const IECore::SmoothSkinningData>( from ); assert( skinningData ); const std::vector<std::string> &influenceNames = skinningData->influenceNames()->readable(); const std::vector<Imath::M44f> &influencePoseData = skinningData->influencePose()->readable(); const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable(); const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable(); const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable(); const std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->readable(); MFnDependencyNode fnSkinClusterNode( to, &s ); MFnSkinCluster fnSkinCluster( to, &s ); if ( s != MS::kSuccess ) { /// \todo: optional parameter to allow custom node types and checks for the necessary attributes /// \todo: create a new skinCluster if we want a kSkinClusterFilter and this isn't one throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: \"%s\" is not a valid skinCluster" ) % fnSkinClusterNode.name() ).str() ); } const unsigned origNumInfluences = influenceNames.size(); unsigned numInfluences = origNumInfluences; std::vector<bool> ignoreInfluence( origNumInfluences, false ); std::vector<int> indexMap( origNumInfluences, -1 ); const bool ignoreMissingInfluences = m_ignoreMissingInfluencesParameter->getTypedValue(); const bool ignoreBindPose = m_ignoreBindPoseParameter->getTypedValue(); // gather the influence objects MObject mObj; MDagPath path; MSelectionList influenceList; MDagPathArray influencePaths; for ( unsigned i=0, index=0; i < origNumInfluences; i++ ) { MString influenceName( influenceNames[i].c_str() ); s = influenceList.add( influenceName ); if ( !s ) { if ( ignoreMissingInfluences ) { ignoreInfluence[i] = true; MGlobal::displayWarning( MString( "ToMayaSkinClusterConverter: \"" + influenceName + "\" is not a valid influence" ) ); continue; } throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: \"%s\" is not a valid influence" ) % influenceName ).str() ); } influenceList.getDependNode( index, mObj ); MFnIkJoint fnInfluence( mObj, &s ); if ( !s ) { if ( ignoreMissingInfluences ) { ignoreInfluence[i] = true; influenceList.remove( index ); MGlobal::displayWarning( MString( "ToMayaSkinClusterConverter: \"" + influenceName + "\" is not a valid influence" ) ); continue; } throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: \"%s\" is not a valid influence" ) % influenceName ).str() ); } fnInfluence.getPath( path ); influencePaths.append( path ); indexMap[i] = index; index++; } MPlugArray connectedPlugs; bool existingBindPose = true; MPlug bindPlug = fnSkinClusterNode.findPlug( "bindPose", true, &s ); if ( !bindPlug.connectedTo( connectedPlugs, true, false ) ) { existingBindPose = false; if ( !ignoreBindPose ) { throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: \"%s\" does not have a valid bindPose" ) % fnSkinClusterNode.name() ).str() ); } } MPlug bindPoseMatrixArrayPlug; MPlug bindPoseMemberArrayPlug; if ( existingBindPose ) { MFnDependencyNode fnBindPose( connectedPlugs[0].node() ); if ( fnBindPose.typeName() != "dagPose" ) { throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: \"%s\" is not a valid bindPose" ) % fnBindPose.name() ).str() ); } bindPoseMatrixArrayPlug = fnBindPose.findPlug( "worldMatrix", true, &s ); bindPoseMemberArrayPlug = fnBindPose.findPlug( "members", true, &s ); } /// \todo: optional parameter to reset the skinCluster's geomMatrix plug // break existing influence connections to the skinCluster MDGModifier dgModifier; MMatrixArray ignoredPreMatrices; MPlug matrixArrayPlug = fnSkinClusterNode.findPlug( "matrix", true, &s ); MPlug bindPreMatrixArrayPlug = fnSkinClusterNode.findPlug( "bindPreMatrix", true, &s ); for ( unsigned i=0; i < matrixArrayPlug.numConnectedElements(); i++ ) { MPlug matrixPlug = matrixArrayPlug.connectionByPhysicalIndex( i, &s ); matrixPlug.connectedTo( connectedPlugs, true, false ); if ( !connectedPlugs.length() ) { continue; } MFnIkJoint fnInfluence( connectedPlugs[0].node() ); fnInfluence.getPath( path ); if ( ignoreMissingInfluences && !influenceList.hasItem( path ) ) { MPlug preMatrixPlug = bindPreMatrixArrayPlug.elementByLogicalIndex( i ); preMatrixPlug.getValue( mObj ); MFnMatrixData matFn( mObj ); ignoredPreMatrices.append( matFn.matrix() ); ignoreInfluence.push_back( false ); indexMap.push_back( influenceList.length() ); influenceList.add( connectedPlugs[0].node() ); numInfluences++; } dgModifier.disconnect( connectedPlugs[0], matrixPlug ); } MPlug lockArrayPlug = fnSkinClusterNode.findPlug( "lockWeights", true, &s ); for ( unsigned i=0; i < lockArrayPlug.numConnectedElements(); i++ ) { MPlug lockPlug = lockArrayPlug.connectionByPhysicalIndex( i, &s ); lockPlug.connectedTo( connectedPlugs, true, false ); if ( connectedPlugs.length() ) { dgModifier.disconnect( connectedPlugs[0], lockPlug ); } } MPlug paintPlug = fnSkinClusterNode.findPlug( "paintTrans", true, &s ); paintPlug.connectedTo( connectedPlugs, true, false ); if ( connectedPlugs.length() ) { dgModifier.disconnect( connectedPlugs[0], paintPlug ); } // break existing influence connections to the bind pose if ( existingBindPose ) { for ( unsigned i=0; i < bindPoseMatrixArrayPlug.numConnectedElements(); i++ ) { MPlug matrixPlug = bindPoseMatrixArrayPlug.connectionByPhysicalIndex( i, &s ); matrixPlug.connectedTo( connectedPlugs, true, false ); if ( connectedPlugs.length() ) { dgModifier.disconnect( connectedPlugs[0], matrixPlug ); } } for ( unsigned i=0; i < bindPoseMemberArrayPlug.numConnectedElements(); i++ ) { MPlug memberPlug = bindPoseMemberArrayPlug.connectionByPhysicalIndex( i, &s ); memberPlug.connectedTo( connectedPlugs, true, false ); if ( connectedPlugs.length() ) { dgModifier.disconnect( connectedPlugs[0], memberPlug ); } } } if ( !dgModifier.doIt() ) { dgModifier.undoIt(); throw IECore::Exception( "ToMayaSkinClusterConverter: Unable to break the influence connections" ); } // make connections from influences to skinCluster and bindPose for ( unsigned i=0; i < numInfluences; i++ ) { if ( ignoreInfluence[i] ) { continue; } int index = indexMap[i]; s = influenceList.getDependNode( index, mObj ); MFnIkJoint fnInfluence( mObj, &s ); MPlug influenceMatrixPlug = fnInfluence.findPlug( "worldMatrix", true, &s ).elementByLogicalIndex( 0, &s ); MPlug influenceMessagePlug = fnInfluence.findPlug( "message", true, &s ); MPlug influenceBindPosePlug = fnInfluence.findPlug( "bindPose", true, &s ); MPlug influenceLockPlug = fnInfluence.findPlug( "lockInfluenceWeights", true, &s ); if ( !s ) { // add the lockInfluenceWeights attribute if it doesn't exist MFnNumericAttribute nAttr; MObject attribute = nAttr.create( "lockInfluenceWeights", "liw", MFnNumericData::kBoolean, false ); fnInfluence.addAttribute( attribute ); influenceLockPlug = fnInfluence.findPlug( "lockInfluenceWeights", true, &s ); } // connect influence to the skinCluster MPlug matrixPlug = matrixArrayPlug.elementByLogicalIndex( index ); MPlug lockPlug = lockArrayPlug.elementByLogicalIndex( index ); dgModifier.connect( influenceMatrixPlug, matrixPlug ); dgModifier.connect( influenceLockPlug, lockPlug ); // connect influence to the bindPose if ( !ignoreBindPose ) { MPlug bindPoseMatrixPlug = bindPoseMatrixArrayPlug.elementByLogicalIndex( index ); MPlug memberPlug = bindPoseMemberArrayPlug.elementByLogicalIndex( index ); dgModifier.connect( influenceMessagePlug, bindPoseMatrixPlug ); dgModifier.connect( influenceBindPosePlug, memberPlug ); } } unsigned firstIndex = find( ignoreInfluence.begin(), ignoreInfluence.end(), false ) - ignoreInfluence.begin(); influenceList.getDependNode( firstIndex, mObj ); MFnDependencyNode fnInfluence( mObj ); MPlug influenceMessagePlug = fnInfluence.findPlug( "message", true, &s ); dgModifier.connect( influenceMessagePlug, paintPlug ); if ( !dgModifier.doIt() ) { dgModifier.undoIt(); throw IECore::Exception( "ToMayaSkinClusterConverter: Unable to create the influence connections" ); } // use influencePoseData as bindPreMatrix for ( unsigned i=0; i < numInfluences; i++ ) { if ( ignoreInfluence[i] ) { continue; } MMatrix preMatrix = ( i < origNumInfluences ) ? IECore::convert<MMatrix>( influencePoseData[i] ) : ignoredPreMatrices[i-origNumInfluences]; MPlug preMatrixPlug = bindPreMatrixArrayPlug.elementByLogicalIndex( indexMap[i], &s ); s = preMatrixPlug.getValue( mObj ); if ( s ) { MFnMatrixData matFn( mObj ); matFn.set( preMatrix ); mObj = matFn.object(); } else { MFnMatrixData matFn; mObj = matFn.create( preMatrix ); } preMatrixPlug.setValue( mObj ); } // remove unneeded bindPreMatrix children unsigned existingElements = bindPreMatrixArrayPlug.numElements(); for ( unsigned i=influenceList.length(); i < existingElements; i++ ) { MPlug preMatrixPlug = bindPreMatrixArrayPlug.elementByLogicalIndex( i, &s ); /// \todo: surely there is a way to accomplish this in c++... MGlobal::executeCommand( ( boost::format( "removeMultiInstance %s" ) % preMatrixPlug.name() ).str().c_str() ); } // get the geometry MObjectArray outputGeoObjs; if ( !fnSkinCluster.getOutputGeometry( outputGeoObjs ) ) { throw IECore::Exception( ( boost::format( "ToMayaSkinClusterConverter: skinCluster \"%s\" does not have any output geometry!" ) % fnSkinCluster.name() ).str() ); } MFnDagNode dagFn( outputGeoObjs[0] ); MDagPath geoPath; dagFn.getPath( geoPath ); // loop through all the points of the geometry and set the weights MItGeometry geoIt( outputGeoObjs[0] ); MPlug weightListArrayPlug = fnSkinClusterNode.findPlug( "weightList", true, &s ); for ( unsigned pIndex=0; !geoIt.isDone(); geoIt.next(), pIndex++ ) { MPlug pointWeightsPlug = weightListArrayPlug.elementByLogicalIndex( pIndex, &s ).child( 0 ); // remove existing influence weight plugs MIntArray existingInfluenceIndices; pointWeightsPlug.getExistingArrayAttributeIndices( existingInfluenceIndices ); for( unsigned i=0; i < existingInfluenceIndices.length(); i++ ) { MPlug influenceWeightPlug = pointWeightsPlug.elementByLogicalIndex( existingInfluenceIndices[i], &s ); MGlobal::executeCommand( ( boost::format( "removeMultiInstance -break 1 %s" ) % influenceWeightPlug.name() ).str().c_str() ); } // add new influence weight plugs int firstIndex = pointIndexOffsets[pIndex]; for( int i=0; i < pointInfluenceCounts[pIndex]; i++ ) { int influenceIndex = pointInfluenceIndices[ firstIndex + i ]; if ( ignoreInfluence[ influenceIndex ] ) { continue; } int skinClusterInfluenceIndex = fnSkinCluster.indexForInfluenceObject( influencePaths[ indexMap[ influenceIndex ] ] ); MPlug influenceWeightPlug = pointWeightsPlug.elementByLogicalIndex( skinClusterInfluenceIndex, &s ); influenceWeightPlug.setValue( pointInfluenceWeights[ firstIndex + i ] ); } } return true; }
MStatus HRBFSkinCluster::deform( MDataBlock& block, MItGeometry& iter, const MMatrix& m, unsigned int multiIndex) // // Method: deform1 // // Description: Deforms the point with a simple smooth skinning algorithm // // Arguments: // block : the datablock of the node // iter : an iterator for the geometry to be deformed // m : matrix to transform the point into world space // multiIndex : the index of the geometry that we are deforming // // { MStatus returnStatus; // get HRBF status MDataHandle HRBFstatusData = block.inputValue(rebuildHRBF, &returnStatus); McheckErr(returnStatus, "Error getting rebuildHRBF handle\n"); int rebuildHRBFStatusNow = HRBFstatusData.asInt(); // handle signaling to the rest of deform that HRBFs must be rebuild bool signalRebuildHRBF = false; signalRebuildHRBF = (rebuildHRBFStatus != rebuildHRBFStatusNow); MMatrixArray bindTFs; // store just the bind transforms in here. MMatrixArray boneTFs; // ALWAYS store just the bone transforms in here. // get HRBF export status MDataHandle exportCompositionData = block.inputValue(exportComposition, &returnStatus); McheckErr(returnStatus, "Error getting exportComposition handle\n"); int exportCompositionStatusNow = exportCompositionData.asInt(); MDataHandle HRBFExportSamplesData = block.inputValue(exportHRBFSamples, &returnStatus); McheckErr(returnStatus, "Error getting exportHRBFSamples handle\n"); std::string exportHRBFSamplesStatusNow = HRBFExportSamplesData.asString().asChar(); MDataHandle HRBFExportValuesData = block.inputValue(exportHRBFValues, &returnStatus); McheckErr(returnStatus, "Error getting exportHRBFValues handle\n"); std::string exportHRBFValuesStatusNow = HRBFExportValuesData.asString().asChar(); // get skinning type MDataHandle useDQData = block.inputValue(useDQ, &returnStatus); McheckErr(returnStatus, "Error getting useDQ handle\n"); int useDQNow = useDQData.asInt(); // determine if we're using HRBF MDataHandle useHRBFData = block.inputValue(useHRBF, &returnStatus); McheckErr(returnStatus, "Error getting useHRBFData handle\n"); int useHRBFnow = useHRBFData.asInt(); // get envelope because why not MDataHandle envData = block.inputValue(envelope, &returnStatus); float env = envData.asFloat(); // get point in space for evaluating HRBF MDataHandle checkHRBFAtData = block.inputValue(checkHRBFAt, &returnStatus); McheckErr(returnStatus, "Error getting useDQ handle\n"); double* data = checkHRBFAtData.asDouble3(); // get the influence transforms // MArrayDataHandle transformsHandle = block.inputArrayValue( matrix ); // tell block what we want int numTransforms = transformsHandle.elementCount(); if ( numTransforms == 0 ) { // no transforms, no problems return MS::kSuccess; } MMatrixArray transforms; // fetch transform matrices -> actual joint matrices for ( int i=0; i<numTransforms; ++i ) { MMatrix worldTF = MFnMatrixData(transformsHandle.inputValue().data()).matrix(); transforms.append(worldTF); boneTFs.append(worldTF); transformsHandle.next(); } // inclusive matrices inverse of the driving transform at time of bind // matrices for transforming vertices to joint local space MArrayDataHandle bindHandle = block.inputArrayValue( bindPreMatrix ); // tell block what we want if ( bindHandle.elementCount() > 0 ) { for ( int i=0; i<numTransforms; ++i ) { MMatrix bind = MFnMatrixData(bindHandle.inputValue().data()).matrix(); transforms[i] = bind * transforms[i]; bindHandle.next(); if (signalRebuildHRBF) bindTFs.append(bind); } } MArrayDataHandle weightListHandle = block.inputArrayValue(weightList); if (weightListHandle.elementCount() == 0) { // no weights - nothing to do std::cout << "no weights!" << std::endl; //rebuildHRBFStatus = rebuildHRBFStatusNow - 1; // HRBFs will need to rebuilt no matter what return MS::kSuccess; } // print HRBF samples if requested if (exportHRBFSamplesStatusNow != exportHRBFSamplesStatus) { std::cout << "instructed to export HRBF samples: " << exportHRBFSamplesStatusNow.c_str() << std::endl; exportHRBFSamplesStatus = exportHRBFSamplesStatusNow; // TODO: handle exporting HRBFs to the text file format hrbfMan->debugSamplesToConsole(exportHRBFSamplesStatus); } // print HRBF values if requested if (exportHRBFValuesStatusNow != exportHRBFValuesStatus) { std::cout << "instructed to export HRBF values: " << exportHRBFValuesStatusNow.c_str() << std::endl; exportHRBFValuesStatus = exportHRBFValuesStatusNow; // TODO: handle exporting HRBFs to the text file format hrbfMan->debugValuesToConsole(exportHRBFValuesStatus); } // print HRBF composition if requested if (exportCompositionStatusNow != exportCompositionStatus) { std::cout << "instructed to export HRBF composition." << std::endl; exportCompositionStatus = exportCompositionStatusNow; // TODO: handle exporting HRBFs to the text file format hrbfMan->debugCompositionToConsole(boneTFs, numTransforms); } // check the HRBF value if the new point is significantly different MPoint checkHRBFHereNow(data[0], data[1], data[2]); if ((checkHRBFHereNow - checkHRBFHere).length() > 0.0001) { if (hrbfMan->m_HRBFs.size() == numTransforms) { std::cout << "checking HRBF at x:" << data[0] << " y: " << data[1] << " z: " << data[2] << std::endl; hrbfMan->compose(boneTFs); float val = 0.0f; float dx = 0.0f; float dy = 0.0f; float dz = 0.0f; float grad = 0.0f; hrbfMan->mf_vals->trilinear(data[0], data[1], data[2], val); hrbfMan->mf_gradX->trilinear(data[0], data[1], data[2], dx); hrbfMan->mf_gradY->trilinear(data[0], data[1], data[2], dy); hrbfMan->mf_gradZ->trilinear(data[0], data[1], data[2], dz); hrbfMan->mf_gradMag->trilinear(data[0], data[1], data[2], grad); std::cout << "val: " << val << " dx: " << dx << " dy: " << dy << " dz: " << dz << " grad: " << grad << std::endl; checkHRBFHere = checkHRBFHereNow; } } // rebuild HRBFs if needed if (signalRebuildHRBF) { std::cout << "instructed to rebuild HRBFs" << std::endl; rebuildHRBFStatus = rebuildHRBFStatusNow; MArrayDataHandle parentIDCsHandle = block.inputArrayValue(jointParentIdcs); // tell block what we want std::vector<int> jointParentIndices(numTransforms); if (parentIDCsHandle.elementCount() > 0) { for (int i = 0; i<numTransforms; ++i) { jointParentIndices[i] = parentIDCsHandle.inputValue().asInt(); parentIDCsHandle.next(); } } MArrayDataHandle jointNamesHandle = block.inputArrayValue(jointNames); // tell block what we want std::vector<std::string> jointNames(numTransforms); if (jointNamesHandle.elementCount() > 0) { for (int i = 0; i<numTransforms; ++i) { jointNames[i] = jointNamesHandle.inputValue().asString().asChar(); jointNamesHandle.next(); } } // debug //std::cout << "got joint hierarchy info! it's:" << std::endl; //for (int i = 0; i < numTransforms; ++i) { // std::cout << i << ": " << jointNames[i].c_str() << " : " << jointParentIndices[i] << std::endl; //} std::cout << "rebuilding HRBFs... " << std::endl; hrbfMan->buildHRBFs(jointParentIndices, jointNames, bindTFs, boneTFs, weightListHandle, iter, weights); std::cout << "done rebuilding!" << std::endl; weightListHandle.jumpToElement(0); // reset this, it's an iterator. trust me. iter.reset(); // reset this iterator so we can go do normal skinning } // perform traditional skinning if (useDQNow != 0) { returnStatus = skinDQ(transforms, numTransforms, weightListHandle, iter); } else { returnStatus = skinLB(transforms, numTransforms, weightListHandle, iter); } // do HRBF corrections if (useHRBFnow != 0) { if (hrbfMan->m_HRBFs.size() == numTransforms) { hrbfMan->compose(boneTFs); iter.reset(); hrbfMan->correct(iter); } } return returnStatus; }