MStatus testNucleusNode::compute(const MPlug &plug, MDataBlock &data) { MStatus stat; if ( plug == nextState ) { //get the value of the currentTime MTime currTime = data.inputValue(currentTime).asTime(); MObject inputData; //pull on start state or current state depending on the current time. if(currTime.value() <= 0.0) { MArrayDataHandle multiDataHandle = data.inputArrayValue(startState); multiDataHandle.jumpToElement(0); inputData =multiDataHandle.inputValue().data(); } else { MArrayDataHandle multiDataHandle = data.inputArrayValue(currentState); multiDataHandle.jumpToElement(0); inputData =multiDataHandle.inputValue().data(); } MFnNObjectData inputNData(inputData); MnCloth * nObj = NULL; inputNData.getObjectPtr(nObj); MFloatPointArray points; nObj->getPositions(points); unsigned int ii; for(ii=0;ii<points.length();ii++) { points[ii].y = (float) sin(points[ii].x + currTime.value()*4.0f*(3.1415f/180.0f)); } nObj->setPositions(points); delete nObj; data.setClean(plug); } else if ( plug == currentState ) { data.setClean(plug); } else if (plug == startState) { data.setClean(plug); } else { stat = MS::kUnknownParameter; } return stat; }
void SurfaceAttach::setOutPlugs(MDataBlock dataBlock, const MFnNurbsSurface &fnSurface, const double dataOffset, const bool dataReverse, const short dataGenus, const double dataStaticLength, const MMatrix &dataParentInverse, const short dataDirection) { MTransformationMatrix tfm; MVector t; MEulerRotation r; MArrayDataHandle outputHandle = dataBlock.outputArrayValue(SurfaceAttach::out); std::int32_t count = outputHandle.elementCount(); MDataHandle o; for (unsigned int k = 0; k < count; ++k) { outputHandle.jumpToElement(k); // Get Transformations tfm = this->matrix(fnSurface, outputHandle.elementIndex(), dataOffset, dataReverse, dataGenus, dataStaticLength, dataParentInverse, dataDirection); t = tfm.translation(MSpace::Space::kWorld); r = tfm.eulerRotation(); o = outputHandle.outputValue(); o.child(SurfaceAttach::translate).set(t); o.child(SurfaceAttach::rotate).set(r.x, r.y, r.z); } // Mark Clean dataBlock.setClean(SurfaceAttach::translate); dataBlock.setClean(SurfaceAttach::rotate); dataBlock.setClean(SurfaceAttach::out); }
MStatus HRBFSkinCluster::skinLB(MMatrixArray& transforms, int numTransforms, MArrayDataHandle& weightListHandle, MItGeometry& iter) { MStatus returnStatus; // Iterate through each point in the geometry. // for (; !iter.isDone(); iter.next()) { MPoint pt = iter.position(); MPoint skinned; // get the weights for this point -> must be dependent on the iterator somehow MArrayDataHandle weightsHandle = weightListHandle.inputValue().child(weights); // compute the skinning -> TODO: what's the order that the weights are given in? Appears to just be maya list relatives order. for (int i = 0; i<numTransforms; ++i) { if (MS::kSuccess == weightsHandle.jumpToElement(i)) { skinned += (pt * transforms[i]) * weightsHandle.inputValue().asDouble(); } } // Set the final position. iter.setPosition(skinned); // advance the weight list handle weightListHandle.next(); } return returnStatus; }
MStatus ffdPlanar::getBoundingBox( MDataBlock& block, unsigned int multiIndex, MBoundingBox &boundingBoxOut ) { MStatus status = MS::kSuccess; MArrayDataHandle inputHandle = block.outputArrayValue( input ); inputHandle.jumpToElement( multiIndex ); MObject mesh = inputHandle.outputValue().child( inputGeom ).asMesh(); MBoundingBox boundingBox = MBoundingBox(); MFnMesh meshFn( mesh, &status ); MCheckErr( status, "Error getting mesh from mesh object\n" ); MPointArray pointArray = MPointArray(); meshFn.getPoints( pointArray, MSpace::kTransform ); for ( int i = 0; i < pointArray.length(); i++ ) { boundingBox.expand( pointArray[i] ); } boundingBoxOut = boundingBox; return status; }
MStatus RippleDeformer::deform(MDataBlock& dataBlock, MItGeometry& itGeo, const MMatrix& localToWorldMatrix, unsigned int geomIndex) { MStatus status; //get attriubtes as a datahandle float env = dataBlock.inputValue(envelope).asFloat(); float amplitude = dataBlock.inputValue(aAmplitude).asFloat(); float displace = dataBlock.inputValue(aDisplace).asFloat(); //get the mesh //retrieve the handle to the input attribute MArrayDataHandle hInput = dataBlock.outputArrayValue(input, &status); CHECK_MSTATUS_AND_RETURN_IT(status); //get the input array index handle status = hInput.jumpToElement(geomIndex); //get the handle of geomIndex attribute MDataHandle hInputElement = hInput.outputValue(&status); //Get the MObject of the input geometry of geomindex MObject oInputGeom = hInputElement.child(inputGeom).asMesh(); MFnMesh fnMesh(oInputGeom, &status); CHECK_MSTATUS_AND_RETURN_IT(status); if (oInputGeom.isNull()) { return MS::kSuccess; } MFloatVectorArray normals; fnMesh.getVertexNormals(false, normals); MPoint pointPos; float weight; for (; !itGeo.isDone(); itGeo.next()) { //get current point position pointPos = itGeo.position(); weight = weightValue(dataBlock, geomIndex, itGeo.index()); pointPos.x = pointPos.x + sin(itGeo.index() + displace) * amplitude * normals[itGeo.index()].x * weight * env; pointPos.y = pointPos.y + sin(itGeo.index() + displace) * amplitude * normals[itGeo.index()].y * weight * env; pointPos.z = pointPos.z + sin(itGeo.index() + displace) * amplitude * normals[itGeo.index()].z * weight * env; //setPosition itGeo.setPosition(pointPos); } return MS::kSuccess; }
// COMPUTE ====================================== MStatus gear_curveCns::deform( MDataBlock& data, MItGeometry& iter, const MMatrix &mat, unsigned int mIndex ) { MStatus returnStatus; MArrayDataHandle adh = data.inputArrayValue( inputs ); int deformer_count = adh.elementCount( &returnStatus ); // Process while (! iter.isDone()){ if (iter.index() < deformer_count){ adh.jumpToElement(iter.index()); MTransformationMatrix m(adh.inputValue().asMatrix() * mat.inverse()); MVector v = m.getTranslation(MSpace::kWorld, &returnStatus ); MPoint pt(v); iter.setPosition(pt); } iter.next(); } return MS::kSuccess; }
MStatus PushDeformer::deform(MDataBlock& dataBlock, MItGeometry& itGeo, const MMatrix& localToWorldMatrix, unsigned int geomIndex) { MStatus status; //get attribute handles double bulgeAmount = dataBlock.inputValue(aAmount, &status).asDouble(); CHECK_MSTATUS_AND_RETURN_IT(status); m_taskData.envelope = dataBlock.inputValue(envelope, &status).asFloat(); CHECK_MSTATUS_AND_RETURN_IT(status); bool useStressV = dataBlock.inputValue(aUseStress, &status).asBool(); CHECK_MSTATUS_AND_RETURN_IT(status); int multiThreadingType = dataBlock.inputValue(aMultiThreadingType, &status).asBool(); CHECK_MSTATUS_AND_RETURN_IT(status); if (m_taskData.envelope <= 0.001) { return MS::kSuccess; } // if the use stress plug is turned on pull MDoubleArray stressV; if (useStressV == true) { //pull out the raw data as an Mobject MObject stressMap = dataBlock.inputValue(aStressMap, &status).data(); CHECK_MSTATUS_AND_RETURN_IT(status); MFnDoubleArrayData stressDataFn(stressMap); m_taskData.stressV = stressDataFn.array(); } //retrieve the handle to the output array attribute MArrayDataHandle hInput = dataBlock.outputArrayValue(input, &status); CHECK_MSTATUS_AND_RETURN_IT(status); //get the input array index handle status = hInput.jumpToElement(geomIndex); //get the handle of geomIndex attribute MDataHandle hInputElement = hInput.outputValue(&status); CHECK_MSTATUS_AND_RETURN_IT(status); //Get the MObject of the input geometry of geomindex MObject oInputGeom = hInputElement.child(inputGeom).asMesh(); MFnMesh fnMesh(oInputGeom, &status); CHECK_MSTATUS_AND_RETURN_IT(status); fnMesh.getVertexNormals(false, m_taskData.normals, MSpace::kWorld); itGeo.allPositions(m_taskData.points, MSpace::kWorld); //MGlobal::displayInfo( "test" ); /*for (int i = 0; i < itGeo.count(); i++) { MGlobal::displayInfo( MFnAttribute(weightList).isArray ); }*/ m_taskData.bulgeAmount = bulgeAmount; if(multiThreadingType == 1) { ThreadData* pThreadData = createThreadData( NUM_TASKS, &m_taskData ); MThreadPool::newParallelRegion( createTasks, (void*)pThreadData ); itGeo.setAllPositions(m_taskData.points); delete [] pThreadData; return MS::kSuccess; } else if(multiThreadingType == 2) { tbb::parallel_for(size_t(0), size_t(itGeo.count()), [this](size_t i) { //const float w = weightValue(dataBlock, geomIndex, i); const float w = 1.0; if (m_taskData.useStressV == true && (m_taskData.stressV.length() > 0)) { //deform m_taskData.points[i] += (MVector(m_taskData.normals[i]) * m_taskData.bulgeAmount * m_taskData.envelope * w * m_taskData.stressV[i]); } else { //deform m_taskData.points[i] += m_taskData.normals[i] * m_taskData.bulgeAmount * m_taskData.envelope * w; } }); } // else if(multiThreadingType == 3) #pragma omp parallel for for (int i = 0; i < itGeo.count(); i++) { float w = weightValue(dataBlock, geomIndex, itGeo.index()); if (useStressV == true && (stressV.length() > 0)) { //deform m_taskData.points[i] += (MVector(m_taskData.normals[i]) * bulgeAmount * m_taskData.envelope * w * m_taskData.stressV[i]); } else { //deform m_taskData.points[i] += m_taskData.normals[i] * bulgeAmount * m_taskData.envelope * w; } } else { for (; !itGeo.isDone(); itGeo.next()) { float w = weightValue(dataBlock, geomIndex, itGeo.index()); if (useStressV == true && (stressV.length() > 0)) { //deform m_taskData.points[itGeo.index()] += (MVector(m_taskData.normals[itGeo.index()]) * bulgeAmount * m_taskData.envelope * w * m_taskData.stressV[itGeo.index()]); } else { //deform m_taskData.points[itGeo.index()] += m_taskData.normals[itGeo.index()] * bulgeAmount * m_taskData.envelope * w; } } } itGeo.setAllPositions(m_taskData.points); return MS::kSuccess; }
MStatus nwayDeformerNode::deform( MDataBlock& data, MItGeometry& itGeo, const MMatrix &localToWorldMatrix, unsigned int mIndex ) { // clock_t clock_start=clock(); MObject thisNode = thisMObject(); MStatus status; MThreadUtils::syncNumOpenMPThreads(); // for OpenMP MArrayDataHandle hBlendMesh = data.inputArrayValue(aBlendMesh); short numIter = data.inputValue( aIteration ).asShort(); short nblendMode = data.inputValue( aBlendMode ).asShort(); short ntetMode = data.inputValue( aTetMode ).asShort(); double visualisationMultiplier = data.inputValue(aVisualisationMultiplier).asDouble(); bool visualiseEnergy = data.inputValue( aVisualiseEnergy ).asBool(); bool nrotationCosistency = data.inputValue( aRotationConsistency ).asBool(); if( nrotationCosistency != rotationCosistency) { numMesh = 0; rotationCosistency = nrotationCosistency; } MPointArray Mpts; itGeo.allPositions(Mpts); int nnumMesh = hBlendMesh.elementCount(); int numPts = Mpts.length(); int numTet = (int)tetList.size()/4; // initialisation if(tetMode != ntetMode) { // clock_t clock_start=clock(); tetMode = ntetMode; numMesh = 0; // point list pts.resize(numPts); for(int i=0; i<numPts; i++) { pts[i] << Mpts[i].x, Mpts[i].y, Mpts[i].z; } std::vector<Matrix4d> P; getMeshData(data, input, inputGeom, mIndex, tetMode, pts, tetList, faceList, edgeList, vertexList, P); dim = removeDegenerate(tetMode, numPts, tetList, faceList, edgeList, vertexList, P); makeAdjacencyList(tetMode, tetList, edgeList, vertexList, adjacencyList); makeTetMatrix(tetMode, pts, tetList, faceList, edgeList, vertexList, P); // prepare ARAP solver numTet = (int)tetList.size()/4; PI.resize(numTet); for(int i=0; i<numTet; i++) { PI[i] = P[i].inverse().eval(); } std::vector<double> tetWeight(numTet,1.0); std::vector< std::map<int,double> > constraint(0); //constraint[0][0]=1.0; isError = ARAPprecompute(PI, tetList, tetWeight, constraint, EPSILON, dim, constraintMat, solver); // MString es="Init timing: "; // double timing=(double)(clock()- clock_start)/CLOCKS_PER_SEC; // es += timing; // MGlobal::displayInfo(es); } if(isError>0) return MS::kFailure; // if blend mesh is added, compute log for each tet logR.resize(nnumMesh); logS.resize(nnumMesh); R.resize(nnumMesh); S.resize(nnumMesh); GL.resize(nnumMesh); logGL.resize(nnumMesh); quat.resize(nnumMesh); L.resize(nnumMesh); // for recomputation of parametrisation if(numMesh>nnumMesh || nblendMode != blendMode) { numMesh =0; blendMode = nblendMode; } for(int j=numMesh; j<nnumMesh; j++) { hBlendMesh.jumpToElement(j); MFnMesh blendMesh(hBlendMesh.inputValue().asMesh()); MPointArray Mbpts; blendMesh.getPoints( Mbpts ); if(numPts != Mbpts.length()) { MGlobal::displayInfo("incompatible mesh"); return MS::kFailure; } std::vector<Vector3d> bpts(numPts); for(int i=0; i<numPts; i++) { bpts[i] << Mbpts[i].x, Mbpts[i].y, Mbpts[i].z; } std::vector<Matrix4d> Q(numTet); makeTetMatrix(tetMode, bpts, tetList, faceList, edgeList, vertexList, Q); logR[j].resize(numTet); logS[j].resize(numTet); R[j].resize(numTet); S[j].resize(numTet); GL[j].resize(numTet); logGL[j].resize(numTet); quat[j].resize(numTet); L[j].resize(numTet); for(int i=0; i<numTet; i++) { Matrix4d aff=PI[i]*Q[i]; GL[j][i]=aff.block(0,0,3,3); L[j][i]=transPart(aff); parametriseGL(GL[j][i], logS[j][i] ,R[j][i]); } if( blendMode == BM_LOG3) { for(int i=0; i<numTet; i++) logGL[j][i]=GL[j][i].log(); } else if( blendMode == BM_SQL) { for(int i=0; i<numTet; i++) { S[j][i]=expSym(logS[j][i]); Quaternion<double> q(R[j][i].transpose()); quat[j][i] << q.x(), q.y(), q.z(), q.w(); } } else if( blendMode == BM_SlRL) { for(int i=0; i<numTet; i++) { S[j][i]=expSym(logS[j][i]); } } // traverse tetrahedra to compute continuous log of rotation if(rotationCosistency) { std::set<int> remain; std::queue<int> later; // load initial rotation from the attr Matrix3d initR; double angle = data.inputValue(aInitRotation).asDouble(); initR << 0,M_PI * angle/180.0,0, -M_PI * angle/180.0,0,0, 0,0,0; std::vector<Matrix3d> prevSO(numTet, initR); // create the adjacency graph to traverse for(int i=0; i<numTet; i++) { remain.insert(remain.end(),i); } while(!remain.empty()) { int next; if( !later.empty()) { next = later.front(); later.pop(); remain.erase(next); } else { next = *remain.begin(); remain.erase(remain.begin()); } logR[j][next]=logSOc(R[j][next],prevSO[next]); for(int k=0; k<adjacencyList[next].size(); k++) { int f=adjacencyList[next][k]; if(remain.erase(f)>0) { prevSO[f]=logR[j][next]; later.push(f); } } } } else { for(int i=0; i<numTet; i++) logR[j][i] = logSO(R[j][i]); } } numMesh=nnumMesh; if(numMesh == 0) return MS::kSuccess; // load weights std::vector<double> weight(numMesh); MArrayDataHandle hWeight = data.inputArrayValue(aWeight); if(hWeight.elementCount() != numMesh) { return MS::kSuccess; } for(int i=0; i<numMesh; i++) { hWeight.jumpToArrayElement(i); weight[i]=hWeight.inputValue().asDouble(); } // compute ideal affine std::vector<Vector3d> new_pts(numPts); std::vector<Matrix4d> A(numTet); std::vector<Matrix3d> AR(numTet),AS(numTet); std::vector<Vector3d> AL(numTet); blendMatList(L, weight, AL); if(blendMode==BM_SRL) { blendMatList(logR, weight, AR); blendMatList(logS, weight, AS); #pragma omp parallel for for(int i=0; i<numTet; i++) { AR[i] = expSO(AR[i]); AS[i] = expSym(AS[i]); } } else if(blendMode == BM_LOG3) { // log blendMatList(logGL, weight, AR); #pragma omp parallel for for(int i=0; i<numTet; i++) { AR[i] = AR[i].exp(); AS[i] = Matrix3d::Identity(); } } else if(blendMode == BM_SQL) { // quaternion std::vector<Vector4d> Aq(numTet); blendMatLinList(S, weight, AS); blendQuatList(quat, weight, Aq); #pragma omp parallel for for(int i=0; i<numTet; i++) { Quaternion<double> Q(Aq[i]); AR[i] = Q.matrix().transpose(); } } else if(blendMode == BM_SlRL) { // expSO+linear Sym blendMatList(logR, weight, AR); blendMatLinList(S, weight, AS); #pragma omp parallel for for(int i=0; i<numTet; i++) { AR[i] = expSO(AR[i]); } } else if(blendMode == BM_AFF) { // linear blendMatLinList(GL, weight, AR); for(int i=0; i<numTet; i++) { AS[i] = Matrix3d::Identity(); } } else { return MS::kFailure; } MatrixXd G(dim+1,3),Sol; std::vector<double> tetEnergy(numTet); // iterate to determine vertices position for(int k=0; k<numIter; k++) { for(int i=0; i<numTet; i++) { A[i]=pad(AS[i]*AR[i],AL[i]); } // solve ARAP std::vector<Vector3d> constraintVector(0); std::vector<double> tetWeight(numTet,1.0); //constraintVector[0]=pts[0]; ARAPSolve(A, PI, tetList, tetWeight, constraintVector, EPSILON, dim, constraintMat, solver, Sol); // set new vertices position for(int i=0; i<numPts; i++) { new_pts[i][0]=Sol(i,0); new_pts[i][1]=Sol(i,1); new_pts[i][2]=Sol(i,2); } // if iteration continues if(k+1<numIter || visualiseEnergy) { std::vector<Matrix4d> Q(numTet); makeTetMatrix(tetMode, new_pts, tetList, faceList, edgeList, vertexList, Q); Matrix3d S,R; #pragma omp parallel for for(int i=0; i<numTet; i++) { polarHigham((PI[i]*Q[i]).block(0,0,3,3), S, AR[i]); tetEnergy[i] = (S-AS[i]).squaredNorm(); } } } // set new vertex position for(int i=0; i<numPts; i++) { Mpts[i].x=Sol(i,0); Mpts[i].y=Sol(i,1); Mpts[i].z=Sol(i,2); } itGeo.setAllPositions(Mpts); // set vertex color according to ARAP energy if(visualiseEnergy) { std::vector<double> ptsEnergy; makePtsWeightList(tetMode, numPts, tetList, faceList, edgeList, vertexList, tetEnergy, ptsEnergy); //double max_energy = *std::max_element(ptsEnergy.begin(), ptsEnergy.end()); outputAttr(data, aEnergy, ptsEnergy); for(int i=0; i<numPts; i++) { ptsEnergy[i] *= visualisationMultiplier; // or /= max_energy } visualise(data, outputGeom, ptsEnergy); } // MString es="Runtime timing: "; // double timing=(double)(clock()- clock_start)/CLOCKS_PER_SEC; // es += timing; // MGlobal::displayInfo(es); return MS::kSuccess; }
void TestDeformer::_deform_on_one_mesh(MDataBlock& data, MItGeometry& iter, const MMatrix& localToWorldMatrix, unsigned int mIndex, MObject &driver_mesh, const MDataHandle &envelopeHandle, MArrayDataHandle &vertMapArrayData, MPointArray &tempOutputPts) { MStatus status; float env = envelopeHandle.asFloat(); // use driver_meshVertIter to walk through the vertex of the current driver mesh MItMeshVertex driver_meshVertIter( driver_mesh, &status ); CHECK_MSTATUS( status ); int i = 0; iter.reset(); while( !iter.isDone(&status) ) { CHECK_MSTATUS( status ); // get the weight float weight = weightValue( data, mIndex, iter.index() ); //painted weight float ww = weight * env; if ( fabs(ww) > FLT_EPSILON )//if ( ww != 0 ) { __debug("%s(), vertMapArrayData.elementCount()=%d, iter.index()=%d", __FUNCTION__, vertMapArrayData.elementCount(), iter.index()); // get index_mapped to which the currrent vertex vI is mapped CHECK_MSTATUS(vertMapArrayData.jumpToElement(iter.index())); int index_mapped = vertMapArrayData.inputValue(&status).asInt(); CHECK_MSTATUS( status ); if( index_mapped >= 0 ) { __debug("index_mapped=%d", index_mapped); int prevInt; CHECK_MSTATUS( driver_meshVertIter.setIndex(index_mapped, prevInt) ); // vertex wrold position on driver mesh MPoint mappedPt = driver_meshVertIter.position( MSpace::kWorld, &status ); CHECK_MSTATUS( status ); // vertex wrold position on driven mesh MPoint iterPt = iter.position(MSpace::kObject, &status) * localToWorldMatrix; CHECK_MSTATUS( status ); // use ww to interpolate between mappedPt and iterPt MPoint pt = iterPt + ((mappedPt - iterPt) * ww ); pt = pt * localToWorldMatrix.inverse(); /// put the deform points to tempOutputPts tempOutputPts[i] += pt; } }//if CHECK_MSTATUS(iter.next()); ++i; }//while }
void TestDeformer::initVertMapping(MDataBlock& data, MItGeometry& iter, const MMatrix& localToWorldMatrix, unsigned int mIndex) { MStatus status; MArrayDataHandle vertMapOutArrayData = data.outputArrayValue( vert_map, &status ); CHECK_MSTATUS( status ); // use vertMapOutArrayBuilder to modify vertMapOutArrayData iter.reset(); int count = iter.count(); MArrayDataBuilder vertMapOutArrayBuilder( vert_map, count, &status ); CHECK_MSTATUS( status ); MPointArray allPts;// world vertex position of the driven mesh allPts.clear(); // walk through the driven mesh /// copy MItGeometry's vertex to vertMapOutArrayData int i = 0; while( !iter.isDone(&status) ) { CHECK_MSTATUS( status ); MDataHandle initIndexDataHnd = vertMapOutArrayBuilder.addElement( i, &status ); CHECK_MSTATUS( status ); int negIndex = -1; initIndexDataHnd.setInt( negIndex ); initIndexDataHnd.setClean(); // append a vertex position(world coordination) to allPts CHECK_MSTATUS(allPts.append( iter.position() * localToWorldMatrix )); i = i+1; iter.next(); } CHECK_MSTATUS(vertMapOutArrayData.set( vertMapOutArrayBuilder )); /// Append more vertex from each driver mesh to vertMapOutArrayData MArrayDataHandle meshAttrHandle = data.inputArrayValue( driver_mesh, &status ); CHECK_MSTATUS( status ); int numMeshes = meshAttrHandle.elementCount(); __debug("%s(), numMeshes=%d", __FUNCTION__, numMeshes); CHECK_MSTATUS(meshAttrHandle.jumpToElement(0)); for( int meshIndex=0; meshIndex < numMeshes; ++meshIndex ) { __debug("%s(), meshIndex=%d", __FUNCTION__, meshIndex); MDataHandle currentMesh = meshAttrHandle.inputValue(&status); CHECK_MSTATUS(status); MObject meshMobj = currentMesh.asMesh(); __debug("%s(), meshMobj.apiTypeStr()=%s", __FUNCTION__, meshMobj.apiTypeStr()); __debugMeshInfo(__FUNCTION__, meshMobj); { _initVertMapping_on_one_mesh(meshMobj, vertMapOutArrayBuilder, allPts);// Note: vertMapOutArrayBuilder is updated in this function! //CHECK_MSTATUS(vertMapOutArrayData.set( vertMapOutArrayBuilder )); } if( !meshAttrHandle.next() ) { break; } }// for (mesh CHECK_MSTATUS(vertMapOutArrayData.set( vertMapOutArrayBuilder )); }
MStatus TestDeformer::deform(MDataBlock& data, MItGeometry& iter, const MMatrix& localToWorldMatrix, unsigned int mIndex) { MStatus status; // get the current node state short initialized_mapping = data.inputValue( initialized_data, &status).asShort(); CHECK_MSTATUS(status); __debug("%s(), initialized_mapping=%d, mIndex=%d", __FUNCTION__, initialized_mapping, mIndex); if( initialized_mapping == 1 ) { initVertMapping(data, iter, localToWorldMatrix, mIndex); // set initialized_data to 2 automatically. User don't have to set it manully. MObject tObj = thisMObject(); MPlug setInitMode = MPlug( tObj, initialized_data ); setInitMode.setShort( 2 ); // and sync initialized_mapping from initialized_data // so, the code section: // if (initialized_mapping == 2) // { // ... // } // will be executed when this deform() function is called next time. initialized_mapping = data.inputValue( initialized_data, &status ).asShort(); CHECK_MSTATUS(status); } if( initialized_mapping == 2 ) { envelope = MPxDeformerNode::envelope; MDataHandle envelopeHandle = data.inputValue( envelope, &status ); CHECK_MSTATUS( status ); MArrayDataHandle vertMapArrayData = data.inputArrayValue( vert_map, &status ); CHECK_MSTATUS( status ); MArrayDataHandle meshAttrHandle = data.inputArrayValue( driver_mesh, &status ); CHECK_MSTATUS( status ); /// 1. init tempOutputPts to zero points MPointArray tempOutputPts; iter.reset(); while( !iter.isDone(&status) ) { CHECK_MSTATUS(tempOutputPts.append(MPoint(0, 0, 0))); CHECK_MSTATUS(iter.next()); } assert(tempOutputPts.length() == iter.count()); /// 2. set tempOutputPts to deform values which comes from each driver mesh iter.reset(); int numMeshes = meshAttrHandle.elementCount(); __debug("%s(), numMeshes=%d", __FUNCTION__, numMeshes); CHECK_MSTATUS(meshAttrHandle.jumpToElement(0)); // for each driver mesh for( int count=0; count < numMeshes; ++count ) { __debug("%s(), count=%d", __FUNCTION__, count); // for one driver mesh: currentMesh MDataHandle currentMesh = meshAttrHandle.inputValue(&status); CHECK_MSTATUS( status ); MObject meshMobj = currentMesh.asMesh(); __debugMeshInfo(__FUNCTION__, meshMobj); // accumulate deform values of currentMesh to tempOutputPts _deform_on_one_mesh(data, iter, localToWorldMatrix, mIndex, meshMobj, envelopeHandle, vertMapArrayData, tempOutputPts ); if( !meshAttrHandle.next() ) { break; } }// for each driver mesh /// 3. add deform value to this geometry(driven mesh) int i = 0; iter.reset(); while( !iter.isDone(&status) ) { MPoint p = iter.position(MSpace::kObject, &status); CHECK_MSTATUS(status); // add the deform value to this vertex CHECK_MSTATUS(iter.setPosition( p + tempOutputPts[i]/numMeshes )); CHECK_MSTATUS(iter.next()); ++i; } assert(tempOutputPts.length() == iter.count()); }// if return( MS::kSuccess ); }
MStatus weightList::compute( const MPlug& plug, MDataBlock& block) { MStatus status = MS::kSuccess; unsigned i, j; MObject thisNode = thisMObject(); MPlug wPlug(thisNode, aWeights); // Write into aWeightList for( i = 0; i < 3; i++) { status = wPlug.selectAncestorLogicalIndex( i, aWeightsList ); MDataHandle wHandle = wPlug.constructHandle(block); MArrayDataHandle arrayHandle(wHandle, &status); McheckErr(status, "arrayHandle construction failed\n"); MArrayDataBuilder arrayBuilder = arrayHandle.builder(&status); McheckErr(status, "arrayBuilder accessing/construction failed\n"); for( j = 0; j < i+2; j++) { MDataHandle handle = arrayBuilder.addElement(j,&status); McheckErr(status, "addElement to arrayBuilder failed\n"); float val = 1.0f*(i+j); handle.set(val); } status = arrayHandle.set(arrayBuilder); McheckErr(status, "set arrayBuilder failed\n"); wPlug.setValue(wHandle); wPlug.destructHandle(wHandle); } // Read from aWeightList and print out result MArrayDataHandle arrayHandle = block.inputArrayValue(aWeightsList, &status); McheckErr(status, "arrayHandle construction for aWeightsList failed\n"); unsigned count = arrayHandle.elementCount(); for( i = 0; i < count; i++) { arrayHandle.jumpToElement(i); MDataHandle eHandle = arrayHandle.inputValue(&status).child(aWeights); McheckErr(status, "handle evaluation failed\n"); MArrayDataHandle eArrayHandle(eHandle, &status); McheckErr(status, "arrayHandle construction for aWeights failed\n"); unsigned eCount = eArrayHandle.elementCount(); for( j = 0; j < eCount; j++) { eArrayHandle.jumpToElement(j); float weight = eArrayHandle.inputValue(&status).asFloat(); McheckErr(status, "weight evaluation error\n"); fprintf(stderr, "weightList[%u][%u] = %g\n",i,j,weight); } } // Read from aWeightList and print out result using the more // efficient jumpToArrayElement() call arrayHandle = block.inputArrayValue(aWeightsList, &status); McheckErr(status, "arrayHandle construction for aWeightsList failed\n"); count = arrayHandle.elementCount(); for( i = 0; i < count; i++) { arrayHandle.jumpToArrayElement(i); MDataHandle eHandle = arrayHandle.inputValue(&status).child(aWeights); McheckErr(status, "handle evaluation failed\n"); MArrayDataHandle eArrayHandle(eHandle, &status); McheckErr(status, "arrayHandle construction for aWeights failed\n"); unsigned eCount = eArrayHandle.elementCount(); for( j = 0; j < eCount; j++) { eArrayHandle.jumpToArrayElement(j); float weight = eArrayHandle.inputValue(&status).asFloat(); McheckErr(status, "weight evaluation error\n"); fprintf(stderr, "weightList[%d][%d] = %g\n",i,j,weight); } } return status; }
MStatus dynExprField::compute(const MPlug& plug, MDataBlock& block) // // Descriptions: // compute output force. // { MStatus status; if( !(plug == mOutputForce) ) return( MS::kUnknownParameter ); // get the logical index of the element this plug refers to. // int multiIndex = plug.logicalIndex( &status ); McheckErr(status, "ERROR in plug.logicalIndex.\n"); // Get input data handle, use outputArrayValue since we do not // want to evaluate both inputs, only the one related to the // requested multiIndex. Evaluating both inputs at once would cause // a dependency graph loop. MArrayDataHandle hInputArray = block.outputArrayValue( mInputData, &status ); McheckErr(status,"ERROR in hInputArray = block.outputArrayValue().\n"); status = hInputArray.jumpToElement( multiIndex ); McheckErr(status, "ERROR: hInputArray.jumpToElement failed.\n"); // get children of aInputData. MDataHandle hCompond = hInputArray.inputValue( &status ); McheckErr(status, "ERROR in hCompond=hInputArray.inputValue\n"); MDataHandle hPosition = hCompond.child( mInputPositions ); MObject dPosition = hPosition.data(); MFnVectorArrayData fnPosition( dPosition ); MVectorArray points = fnPosition.array( &status ); McheckErr(status, "ERROR in fnPosition.array(), not find points.\n"); // Comment out the following since velocity, and mass are // not needed in this field. // // MDataHandle hVelocity = hCompond.child( mInputVelocities ); // MObject dVelocity = hVelocity.data(); // MFnVectorArrayData fnVelocity( dVelocity ); // MVectorArray velocities = fnVelocity.array( &status ); // McheckErr(status, "ERROR in fnVelocity.array(), not find velocities.\n"); // // MDataHandle hMass = hCompond.child( mInputMass ); // MObject dMass = hMass.data(); // MFnDoubleArrayData fnMass( dMass ); // MDoubleArray masses = fnMass.array( &status ); // McheckErr(status, "ERROR in fnMass.array(), not find masses.\n"); // The attribute mInputPPData contains the attribute in an array form // parpared by the particleShape if the particleShape has per particle // attribute fieldName_attrName. // // Suppose a field with the name dynExprField1 is connecting to // particleShape1, and the particleShape1 has per particle float attribute // dynExprField1_magnitude and vector attribute dynExprField1_direction, // then hInputPPArray will contains a MdoubleArray with the corresponding // name "magnitude" and a MvectorArray with the name "direction". This // is a mechanism to allow the field attributes being driven by dynamic // expression. MArrayDataHandle mhInputPPData = block.inputArrayValue( mInputPPData, &status ); McheckErr(status,"ERROR in mhInputPPData = block.inputArrayValue().\n"); status = mhInputPPData.jumpToElement( multiIndex ); McheckErr(status, "ERROR: mhInputPPArray.jumpToElement failed.\n"); MDataHandle hInputPPData = mhInputPPData.inputValue( &status ); McheckErr(status, "ERROR in hInputPPData = mhInputPPData.inputValue\n"); MObject dInputPPData = hInputPPData.data(); MFnArrayAttrsData inputPPArray( dInputPPData ); MDataHandle hOwnerPPData = block.inputValue( mOwnerPPData, &status ); McheckErr(status, "ERROR in hOwnerPPData = block.inputValue\n"); MObject dOwnerPPData = hOwnerPPData.data(); MFnArrayAttrsData ownerPPArray( dOwnerPPData ); const MString magString("magnitude"); MFnArrayAttrsData::Type doubleType(MFnArrayAttrsData::kDoubleArray); bool arrayExist; MDoubleArray magnitudeArray; arrayExist = inputPPArray.checkArrayExist(magString, doubleType, &status); // McheckErr(status, "ERROR in checkArrayExist(magnitude)\n"); if(arrayExist) { magnitudeArray = inputPPArray.getDoubleData(magString, &status); // McheckErr(status, "ERROR in inputPPArray.doubleArray(magnitude)\n"); } MDoubleArray magnitudeOwnerArray; arrayExist = ownerPPArray.checkArrayExist(magString, doubleType, &status); // McheckErr(status, "ERROR in checkArrayExist(magnitude)\n"); if(arrayExist) { magnitudeOwnerArray = ownerPPArray.getDoubleData(magString, &status); // McheckErr(status, "ERROR in ownerPPArray.doubleArray(magnitude)\n"); } const MString dirString("direction"); MFnArrayAttrsData::Type vectorType(MFnArrayAttrsData::kVectorArray); arrayExist = inputPPArray.checkArrayExist(dirString, vectorType, &status); MVectorArray directionArray; // McheckErr(status, "ERROR in checkArrayExist(direction)\n"); if(arrayExist) { directionArray = inputPPArray.getVectorData(dirString, &status); // McheckErr(status, "ERROR in inputPPArray.vectorArray(direction)\n"); } arrayExist = ownerPPArray.checkArrayExist(dirString, vectorType, &status); MVectorArray directionOwnerArray; // McheckErr(status, "ERROR in checkArrayExist(direction)\n"); if(arrayExist) { directionOwnerArray = ownerPPArray.getVectorData(dirString, &status); // McheckErr(status, "ERROR in ownerPPArray.vectorArray(direction)\n"); } // Compute the output force. // MVectorArray forceArray; apply( block, points.length(), magnitudeArray, magnitudeOwnerArray, directionArray, directionOwnerArray, forceArray ); // get output data handle // MArrayDataHandle hOutArray = block.outputArrayValue( mOutputForce, &status); McheckErr(status, "ERROR in hOutArray = block.outputArrayValue.\n"); MArrayDataBuilder bOutArray = hOutArray.builder( &status ); McheckErr(status, "ERROR in bOutArray = hOutArray.builder.\n"); // get output force array from block. // MDataHandle hOut = bOutArray.addElement(multiIndex, &status); McheckErr(status, "ERROR in hOut = bOutArray.addElement.\n"); MFnVectorArrayData fnOutputForce; MObject dOutputForce = fnOutputForce.create( forceArray, &status ); McheckErr(status, "ERROR in dOutputForce = fnOutputForce.create\n"); // update data block with new output force data. // hOut.set( dOutputForce ); block.setClean( plug ); return( MS::kSuccess ); }
MStatus CageDeformerNode::deform( MDataBlock& data, MItGeometry& itGeo, const MMatrix &localToWorldMatrix, unsigned int mIndex ) { /// main MStatus status; MThreadUtils::syncNumOpenMPThreads(); // for OpenMP // load cage mesh and other attributes MObject oCageMesh = data.inputValue( aCageMesh ).asMesh(); short blendMode = data.inputValue(aBlendMode).asShort(); bool rotationCosistency = data.inputValue( aRotationConsistency ).asBool(); bool frechetSum = data.inputValue( aFrechetSum ).asBool(); short newConstraintMode = data.inputValue(aConstraintMode).asShort(); double newConstraintWeight = data.inputValue( aConstraintWeight ).asDouble(); if ( oCageMesh.isNull() || blendMode == 99) return MS::kSuccess; short newCageMode = data.inputValue(aCageMode).asShort(); MFnMesh fnCageMesh( oCageMesh, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MPointArray cagePoints; fnCageMesh.getPoints( cagePoints, MSpace::kWorld ); // save initial cage state if (initCagePoints.length() != cagePoints.length()){ initCageMesh = oCageMesh; initCagePoints=cagePoints; } // when cage mode is changed if(newCageMode != cageMode || newConstraintMode != constraintMode || newConstraintWeight != constraintWeight) { cageMode = newCageMode; constraintMode = newConstraintMode; constraintWeight = newConstraintWeight; std::vector<double> tetWeight; // read target mesh data MArrayDataHandle hInput = data.outputArrayValue( input, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); status = hInput.jumpToElement( mIndex ); CHECK_MSTATUS_AND_RETURN_IT( status ); MObject oInputGeom = hInput.outputValue().child( inputGeom ).asMesh(); MFnMesh inputMesh(oInputGeom); inputMesh.getPoints( pts ); numPts=pts.length(); for(int j=0; j<numPts; j++ ) pts[j] *= localToWorldMatrix; MIntArray count; inputMesh.getTriangles( count, meshTriangles ); numTet=meshTriangles.length()/3; std::vector<Matrix4d> P(numTet); tetCenter.resize(numTet); tetMatrixC(pts, meshTriangles, P, tetCenter); PI.resize(numTet); for(int i=0;i<numTet;i++) PI[i] = P[i].inverse(); // prepare cage tetrahedra MFnMesh fnInitCageMesh( initCageMesh, &status ); if(cageMode == 10 || cageMode == 11) // face mode { if(cageMode == 10){ // triangulate faces by MAYA standard MIntArray count; fnInitCageMesh.getTriangles( count, triangles ); tetWeight.resize(triangles.length()/3, 1.0f); }else if(cageMode ==11){ // trianglate faces with more than 3 edges in a symmetric way triangles.clear(); MItMeshPolygon iter(initCageMesh); MIntArray tmp; MVector normal; tetWeight.reserve(4*iter.count()); unsigned int l; for(unsigned int i=0; ! iter.isDone(); i++){ iter.getVertices(tmp); l=tmp.length(); if(l==3){ tetWeight.push_back(1.0); triangles.append(tmp[0]); triangles.append(tmp[1]); triangles.append(tmp[2]); }else{ for(unsigned int j=0;j<l;j++){ tetWeight.push_back((l-2.0)/l); triangles.append(tmp[j]); triangles.append(tmp[(j+1) % l]); triangles.append(tmp[(j+2) % l]); } } iter.next(); } } // face mode compute init matrix numPrb=triangles.length()/3; initMatrix.resize(numPrb); tetMatrix(initCagePoints, triangles, cageMode, initMatrix); // compute weight w.resize(numTet); std::vector< std::vector<double> > idist(numTet); for(int j=0;j<numTet;j++){ idist[j].resize(numPrb); w[j].resize(numPrb); double sidist = 0.0; for(int i=0;i<numPrb;i++){ idist[j][i] = tetWeight[i]/distPtTri(tetCenter[j],initMatrix[i]); sidist += idist[j][i]; } assert(sidist>0.0f); for(int i=0;i<numPrb;i++) w[j][i] = idist[j][i] /sidist; }// face mode end }else if(cageMode == 0 || cageMode == 1){ // vertex mode triangles.clear(); std::vector<int> tetCount(initCagePoints.length()); MItMeshVertex iter(initCageMesh); for(int j=0; ! iter.isDone(); j++){ MIntArray v; iter.getConnectedVertices(v); // at each vertex, construct tetrahedra from connected edges int l=v.length(); if(l==3){ if(isDegenerate(initCagePoints[j],initCagePoints[v[0]],initCagePoints[v[1]],initCagePoints[v[2]]) != 0){ tetCount[j]++; triangles.append(j); triangles.append(v[0]); triangles.append(v[1]); triangles.append(v[2]); } }else{ for(int k=0;k<l;k++){ if(isDegenerate(initCagePoints[j],initCagePoints[v[k]],initCagePoints[v[(k+1) % l]],initCagePoints[v[(k+2) % l]]) != 0){ tetCount[j]++; triangles.append(j); triangles.append(v[k]); triangles.append(v[(k+1) % l]); triangles.append(v[(k+2) % l]); } } } iter.next(); } numPrb=triangles.length()/4; initMatrix.resize(numPrb); tetMatrix(initCagePoints, triangles, cageMode, initMatrix); // vertex mode compute weight w.resize(numTet); std::vector< std::vector<double> > idist(numTet); tetWeight.resize(numPrb); for(int i=0;i<numPrb;i++) tetWeight[i]=1.0/(double)tetCount[triangles[4*i]]; for(int j=0;j<numTet;j++){ idist[j].resize(numPrb); w[j].resize(numPrb); double sidist = 0.0; for(int i=0;i<numPrb;i++){ Vector3d c(initCagePoints[triangles[4*i]].x,initCagePoints[triangles[4*i]].y,initCagePoints[triangles[4*i]].z); idist[j][i] = tetWeight[i] / ((tetCenter[j]-c).squaredNorm()); sidist += idist[j][i]; } assert(sidist>0.0f); for(int i=0;i<numPrb;i++) w[j][i] = idist[j][i] /sidist; } }else if(cageMode == 5 || cageMode == 6 ){ // vertex averaged normal mode triangles.clear(); std::vector<int> tetCount(initCagePoints.length()); MItMeshVertex iter(initCageMesh); for(int j=0; ! iter.isDone(); j++){ MIntArray v; iter.getConnectedVertices(v); int l=v.length(); for(int k=0;k<l;k++){ tetCount[j]++; triangles.append(j); triangles.append(v[k]); triangles.append(v[(k+1) % l]); } iter.next(); } numPrb=triangles.length()/3; initMatrix.resize(numPrb); tetMatrix(initCagePoints, triangles, cageMode, initMatrix); // vertex mode compute weight w.resize(numTet); std::vector< std::vector<double> > idist(numTet); tetWeight.resize(numPrb); for(int i=0;i<numPrb;i++) tetWeight[i]=1.0/(double)tetCount[triangles[3*i]]; for(int j=0;j<numTet;j++){ idist[j].resize(numPrb); w[j].resize(numPrb); double sidist = 0.0; for(int i=0;i<numPrb;i++){ Vector3d c(initCagePoints[triangles[3*i]].x,initCagePoints[triangles[3*i]].y,initCagePoints[triangles[3*i]].z); idist[j][i] = tetWeight[i] / ((tetCenter[j]-c).squaredNorm()); sidist += idist[j][i]; } assert(sidist>0.0f); for(int i=0;i<numPrb;i++) w[j][i] = idist[j][i] /sidist; } }// end of cage setup // find constraint points if(constraintMode == 1){ numConstraint = numPrb; }else{ numConstraint = 1; // at least one constraint is necessary to determine global translation } constraintTet.resize(numConstraint); constraintVector.resize(numConstraint); // for each cage tetrahedra, constraint the point on the mesh with largest weight for(int i=0;i<numConstraint;i++){ constraintTet[i] = 0; for(int j=1;j<numTet;j++){ if(w[j][i] > w[constraintTet[i]][i]){ constraintTet[i] = j; } } constraintVector[i] << tetCenter[constraintTet[i]](0), tetCenter[constraintTet[i]](1), tetCenter[constraintTet[i]](2), 1.0; } // precompute arap solver arapHI(PI, meshTriangles); } // compute deformation if( ! rotationCosistency || numPrb != prevNs.size()){ // clear previous rotation prevThetas.clear(); prevThetas.resize(numPrb, 0.0); prevNs.clear(); prevNs.resize(numPrb, Vector3d::Zero()); } // find affine transformations for tetrahedra std::vector<Matrix4d> cageMatrix(numPrb), SE(numPrb), logSE(numPrb),logAff(numPrb),aff(numPrb); std::vector<Matrix3d> logR(numPrb),R(numPrb),logS(numPrb),logGL(numPrb); std::vector<Vector3d> L(numPrb); std::vector<Vector4d> quat(numPrb); tetMatrix(cagePoints, triangles, cageMode, cageMatrix); for(int i=0; i<numPrb; i++) aff[i]=initMatrix[i].inverse()*cageMatrix[i]; // compute parametrisation if(blendMode == 0 || blendMode == 1 || blendMode == 5) // polarexp or quaternion { for(unsigned int i=0;i<numPrb;i++){ parametriseGL(aff[i].block(0,0,3,3), logS[i] ,R[i]); L[i] = transPart(aff[i]); if(blendMode == 0){ // Rotational log logR[i]=logSOc(R[i], prevThetas[i], prevNs[i]); }else if(blendMode == 1){ // Eucledian log SE[i]=affine(R[i], L[i]); logSE[i]=logSEc(SE[i], prevThetas[i], prevNs[i]); }else if(blendMode == 5){ // quaternion Quaternion<double> Q(R[i].transpose()); quat[i] << Q.x(), Q.y(), Q.z(), Q.w(); } } }else if(blendMode == 2){ //logmatrix3 for(unsigned int i=0;i<numPrb;i++){ logGL[i] = aff[i].block(0,0,3,3).log(); L[i] = transPart(aff[i]); } }else if(blendMode == 3){ // logmatrix4 for(unsigned int i=0;i<numPrb;i++){ logAff[i] = aff[i].log(); } } // compute blended matrices #pragma omp parallel for std::vector<Matrix4d> At(numTet); for(int j=0; j<numTet; j++ ){ if(blendMode==0){ Matrix3d RR=Matrix3d::Zero(); Matrix3d SS=Matrix3d::Zero(); Vector3d l=Vector3d::Zero(); for(unsigned int i=0; i<numPrb; i++){ RR += w[j][i] * logR[i]; SS += w[j][i] * logS[i]; l += w[j][i] * L[i]; } SS = expSym(SS); if(frechetSum){ RR = frechetSO(R, w[j]); }else{ RR = expSO(RR); } At[j] = affine(SS*RR, l); }else if(blendMode==1){ // rigid transformation Matrix4d EE=Matrix4d::Zero(); Matrix3d SS=Matrix3d::Zero(); for(unsigned int i=0; i<numPrb; i++){ EE += w[j][i] * logSE[i]; SS += w[j][i] * logS[i]; } if(frechetSum){ EE = frechetSE(SE, w[j]); }else{ EE = expSE(EE); } At[j] = affine(expSym(SS),Vector3d::Zero())*EE; }else if(blendMode == 2){ //logmatrix3 Matrix3d G=Matrix3d::Zero(); Vector3d l=Vector3d::Zero(); for(unsigned int i=0; i<numPrb; i++){ G += w[j][i] * logGL[i]; l += w[j][i] * L[i]; } At[j] = affine(G.exp(), l); }else if(blendMode == 3){ // logmatrix4 Matrix4d A=Matrix4d::Zero(); for(unsigned int i=0; i<numPrb; i++) A += w[j][i] * logAff[i]; At[j] = A.exp(); }else if(blendMode == 5){ // quaternion Vector4d q=Vector4d::Zero(); Matrix3d SS=Matrix3d::Zero(); Vector3d l=Vector3d::Zero(); for(unsigned int i=0; i<numPrb; i++){ q += w[j][i] * quat[i]; SS += w[j][i] * logS[i]; l += w[j][i] * L[i]; } SS = expSym(SS); Quaternion<double> Q(q); Matrix3d RR = Q.matrix().transpose(); At[j] = affine(SS*RR, l); }else if(blendMode==10){ At[j] = Matrix4d::Zero(); for(unsigned int i=0; i<numPrb; i++){ At[j] += w[j][i] * aff[i]; } } } // compute target vertices position MatrixXd G=MatrixXd::Zero(numTet+numPts,3); arapG(At, PI, meshTriangles, aff, G); MatrixXd Sol = solver.solve(G); for(unsigned int i=0;i<numPts;i++){ pts[i].x=Sol(i,0); pts[i].y=Sol(i,1); pts[i].z=Sol(i,2); pts[i] *= localToWorldMatrix.inverse(); } itGeo.setAllPositions(pts); return MS::kSuccess; }
MStatus puttyNode::deform( MDataBlock& block, MItGeometry& iter, const MMatrix& worldMatrix, unsigned int multiIndex) { // MGlobal::displayInfo("deform"); MStatus status = MS::kSuccess; ///////////////////////////////////////////////////////////////////////////////////////////////// // // get inputs // // get the node ready flag MDataHandle dh = block.inputValue(aScriptSourced,&status); SYS_ERROR_CHECK(status, "Error getting aScriptSourced data handle\n"); bool scriptSourced = dh.asBool(); if (!scriptSourced) return MS::kSuccess; dh = block.inputValue(aNodeReady,&status); SYS_ERROR_CHECK(status, "Error getting node ready data handle\n"); bool nodeReady = dh.asBool(); // if it's not ready, don't do anything if (!nodeReady) return MS::kSuccess; dh = block.inputValue(aDefSpace,&status); SYS_ERROR_CHECK(status, "Error getting defSpace data handle\n"); short defSpace = dh.asShort(); dh = block.inputValue(aDefWeights,&status); SYS_ERROR_CHECK(status, "Error getting defWeights data handle\n"); short defWeights = dh.asShort(); dh = block.inputValue(aDefEnvelope,&status); SYS_ERROR_CHECK(status, "Error getting defEnvelope data handle\n"); short defEnvelope = dh.asShort(); // get the command dh = block.inputValue(aCmdBaseName,&status); SYS_ERROR_CHECK(status, "Error getting aCmdBaseName handle\n"); MString script = dh.asString(); /* if (script == "") { status = MS::kFailure; USER_ERROR_CHECK(status, "no script provided!\n"); } */ ///////////////////////////////////////////////////////////////////////////////////////////////// // // build mel cmd string // // check if it's a valid cmd // get the envelope // double env = 1; if (defEnvelope == MSD_ENVELOPE_AUTO) { dh = block.inputValue(envelope,&status); SYS_ERROR_CHECK(status, "Error getting envelope data handle\n"); env = double(dh.asFloat()); // early stop 'cause there is nothing more to do if (env == 0.0) return MS::kSuccess; } // get the points, transform them into the right space if needed // int count = iter.count(); MVectorArray points(count); for ( ; !iter.isDone(); iter.next()) points[iter.index()] = iter.position(); if ( defSpace == MSD_SPACE_WORLD ) { for (int i = 0;i<count;i++) points[i] = MPoint(points[i]) * worldMatrix; } // get the weights // MDoubleArray weights; if ( defWeights == MSD_WEIGHTS_AUTO) { weights.setLength(count); for (int i = 0;i<count;i++) weights[i] = weightValue(block,multiIndex,i); } // get the object name and type // get the input geometry, traverse through the data handles MArrayDataHandle adh = block.outputArrayValue( input, &status ); SYS_ERROR_CHECK(status,"error getting input array data handle.\n"); status = adh.jumpToElement( multiIndex ); SYS_ERROR_CHECK(status, "input jumpToElement failed.\n"); // compound data MDataHandle cdh = adh.inputValue( &status ); SYS_ERROR_CHECK(status, "error getting input inputValue\n"); // input geometry child dh = cdh.child( inputGeom ); MObject dInputGeometry = dh.data(); // get the type MString geometryType = dInputGeometry.apiTypeStr(); // get the name // MFnDagNode dagFn( dInputGeometry, &status); // SYS_ERROR_CHECK(status, "error converting geometry obj to dag node\n"); // MString geometryName = dagFn.fullPathName(&status); // SYS_ERROR_CHECK(status, "error getting full path name \n"); // MString geometryType = ""; // MString geometryName = ""; ///////////////////////////////////////////////////////////////////////////////////////////////// // // set the current values on the temp plugs for the script to be picked up // // the position MObject thisNode = thisMObject(); MPlug currPlug(thisNode,aCurrPosition); MFnVectorArrayData vecD; MObject currObj = vecD.create(points,&status); currPlug.setValue(currObj); SYS_ERROR_CHECK(status, "error setting currPosPlug value\n"); // the weights currPlug =MPlug(thisNode,aCurrWeight); MFnDoubleArrayData dblD; currObj = dblD.create(weights,&status); currPlug.setValue(currObj); SYS_ERROR_CHECK(status, "error setting currWeightsPlug value\n"); // world matrix currPlug =MPlug(thisNode,aCurrWorldMatrix); MFnMatrixData matD; currObj = matD.create(worldMatrix,&status); currPlug.setValue(currObj); SYS_ERROR_CHECK(status, "error setting currWorldMatrixPlug value\n"); // the multi index currPlug =MPlug(thisNode,aCurrMultiIndex); currPlug.setValue(int(multiIndex)); SYS_ERROR_CHECK(status, "error setting currMultiIndexPlug value\n"); // geometry name/type // currPlug =MPlug(thisNode,aCurrGeometryName); // currPlug.setValue(geometryName); // SYS_ERROR_CHECK(status, "error setting aCurrGeometryName value\n"); currPlug =MPlug(thisNode,aCurrGeometryType); currPlug.setValue(geometryType); SYS_ERROR_CHECK(status, "error setting aCurrGeometryType value\n"); ///////////////////////////////////////////////////////////////////////////////////////////////// // // execute the mel script // MString melCmd = script+"(\"" +name()+"\","+count+")"; MCommandResult melResult; status = MGlobal::executeCommand(melCmd,melResult); // if the command did not work, then try to resource the script // (might have been that we were in a fresh scene and nothing was ready yet if (status != MS::kSuccess) { dh = block.inputValue(aScript,&status); SYS_ERROR_CHECK(status, "Error getting aCmdBaseName handle\n"); MString scriptFile = dh.asString(); // try to source the script MString cmd = "source \"" + scriptFile+"\""; MCommandResult melResult; status = MGlobal::executeCommand(cmd,melResult); // if successfull, retry the command if (!status.error()) { status = MGlobal::executeCommand(melCmd,melResult); } } USER_ERROR_CHECK(status, "Error executing mel command, please check the function you provided is valid, error free and has the appropriate parameters!"); // check the result type if ((melResult.resultType()) != (MCommandResult::kDoubleArray)) { USER_ERROR_CHECK(MS::kFailure, "result of mel command has wrong type, should be doubleArray (which will be interpreted as vectorArray)!"); } // get the result as a double array MDoubleArray newP; status = melResult.getResult(newP); USER_ERROR_CHECK(status, "Error getting result of mel command!"); int newCount = newP.length()/3; // size check if (newCount != count) { USER_ERROR_CHECK(MS::kFailure, "the size of the result does not match the size of the input!"); } // convert the double array into a vector array MPointArray newPoints(newCount); for(int i=0;i<newCount;i++) newPoints[i]=MPoint(newP[i*3],newP[i*3+1],newP[i*3+2]); ///////////////////////////////////////////////////////////////////////////////////////////////// // // interprete and apply the result // // do the envelope and weights if ((defEnvelope == MSD_ENVELOPE_AUTO)||((defWeights == MSD_WEIGHTS_AUTO))) { MDoubleArray envPP(count, env); if (defWeights == MSD_WEIGHTS_AUTO) { for (int i = 0;i<count;i++) envPP[i] *= weights[i]; } // linear interpolation between old and new points for (int i = 0;i<count;i++) newPoints[i] = (points[i] * (1-envPP[i])) + (newPoints[i] * envPP[i]); } // retransform the result if it was in world space if ( defSpace == MSD_SPACE_WORLD ) { MMatrix worldMatrixInv = worldMatrix.inverse(); for (int i = 0;i<count;i++) newPoints[i] *= worldMatrixInv; } // set the points iter.reset(); for ( ; !iter.isDone(); iter.next()) iter.setPosition(newPoints[iter.index()]); return status; }
MStatus probeDeformerARAPNode::deform( MDataBlock& data, MItGeometry& itGeo, const MMatrix &localToWorldMatrix, unsigned int mIndex ) { MObject thisNode = thisMObject(); MStatus status; MThreadUtils::syncNumOpenMPThreads(); // for OpenMP bool worldMode = data.inputValue( aWorldMode ).asBool(); bool areaWeighted = data.inputValue( aAreaWeighted ).asBool(); short stiffnessMode = data.inputValue( aStiffness ).asShort(); short blendMode = data.inputValue( aBlendMode ).asShort(); short tetMode = data.inputValue( aTetMode ).asShort(); short numIter = data.inputValue( aIteration ).asShort(); short constraintMode = data.inputValue( aConstraintMode ).asShort(); short visualisationMode = data.inputValue( aVisualisationMode ).asShort(); mesh.transWeight = data.inputValue( aTransWeight ).asDouble(); double constraintWeight = data.inputValue( aConstraintWeight ).asDouble(); double normExponent = data.inputValue( aNormExponent ).asDouble(); double visualisationMultiplier = data.inputValue(aVisualisationMultiplier).asDouble(); MArrayDataHandle hMatrixArray = data.inputArrayValue(aMatrix); MArrayDataHandle hInitMatrixArray = data.inputArrayValue(aInitMatrix); // check connection if(hMatrixArray.elementCount() > hInitMatrixArray.elementCount() || hMatrixArray.elementCount() == 0 || blendMode == BM_OFF){ return MS::kSuccess; }else if(hMatrixArray.elementCount() < hInitMatrixArray.elementCount()){ std::set<int> indices; for(int i=0;i<hInitMatrixArray.elementCount();i++){ hInitMatrixArray.jumpToArrayElement(i); indices.insert(hInitMatrixArray.elementIndex()); } for(int i=0;i<hMatrixArray.elementCount();i++){ hMatrixArray.jumpToArrayElement(i); indices.erase(hMatrixArray.elementIndex()); } deleteAttr(data, aInitMatrix, indices); deleteAttr(data, aProbeConstraintRadius, indices); deleteAttr(data, aProbeWeight, indices); } bool isNumProbeChanged = (numPrb != hMatrixArray.elementCount()); numPrb = hMatrixArray.elementCount(); B.setNum(numPrb); // read matrices from probes std::vector<Matrix4d> initMatrix(numPrb), matrix(numPrb); readMatrixArray(hInitMatrixArray, initMatrix); readMatrixArray(hMatrixArray, matrix); // read vertex positions MPointArray Mpts; itGeo.allPositions(Mpts); int numPts = Mpts.length(); // compute distance if(!data.isClean(aARAP) || !data.isClean(aComputeWeight) || isNumProbeChanged){ // load points list if(worldMode){ for(int j=0; j<numPts; j++ ) Mpts[j] *= localToWorldMatrix; } pts.resize(numPts); for(int i=0;i<numPts;i++){ pts[i] << Mpts[i].x, Mpts[i].y, Mpts[i].z; } // make tetrahedral structure getMeshData(data, input, inputGeom, mIndex, tetMode, pts, mesh.tetList, faceList, edgeList, vertexList, mesh.tetMatrix, mesh.tetWeight); mesh.dim = removeDegenerate(tetMode, numPts, mesh.tetList, faceList, edgeList, vertexList, mesh.tetMatrix); makeTetMatrix(tetMode, pts, mesh.tetList, faceList, edgeList, vertexList, mesh.tetMatrix, mesh.tetWeight); makeTetCenterList(tetMode, pts, mesh.tetList, tetCenter); mesh.numTet = (int)mesh.tetList.size()/4; mesh.computeTetMatrixInverse(); // initial probe position for(int i=0;i<numPrb;i++){ B.centre[i] = transPart(initMatrix[i]); } // compute distance between probe and tetrahedra D.setNum(numPrb, numPts, mesh.numTet); D.computeDistTet(tetCenter, B.centre); D.findClosestTet(); D.computeDistPts(pts, B.centre); D.findClosestPts(); if(!areaWeighted){ mesh.tetWeight.clear(); mesh.tetWeight.resize(mesh.numTet,1.0); } } // (re)compute ARAP if(!data.isClean(aARAP) || isNumProbeChanged){ // load painted weights if(stiffnessMode == SM_PAINT) { VectorXd ptsWeight(numPts); for (int i=0; !itGeo.isDone(); itGeo.next()){ double w=weightValue(data, mIndex, itGeo.index()); ptsWeight[i++] = (w>EPSILON) ? w : EPSILON; } makeTetWeightList(tetMode, mesh.tetList, faceList, edgeList, vertexList, ptsWeight, mesh.tetWeight); }else if(stiffnessMode == SM_LEARN) { std::vector<double> tetEnergy(mesh.numTet,0); MArrayDataHandle hSupervisedMesh = data.inputArrayValue(aSupervisedMesh); int numSupervisedMesh = hSupervisedMesh.elementCount(); for(int j=0;j<numSupervisedMesh;j++){ hSupervisedMesh.jumpToElement(j); MFnMesh ex_mesh(hSupervisedMesh.inputValue().asMesh()); MPointArray Mspts; ex_mesh.getPoints( Mspts ); if(numPts != Mspts.length()){ MGlobal::displayInfo("incompatible mesh"); return MS::kFailure; } std::vector<Vector3d> spts(numPts); for(int i=0;i<numPts;i++){ spts[i] << Mspts[i].x, Mspts[i].y, Mspts[i].z; } std::vector<double> dummy_weight; makeTetMatrix(tetMode, spts, mesh.tetList, faceList, edgeList, vertexList, Q, dummy_weight); Matrix3d S,R; for(int i=0;i<mesh.numTet;i++) { polarHigham((mesh.tetMatrixInverse[i]*Q[i]).block(0,0,3,3), S, R); tetEnergy[i] += (S-Matrix3d::Identity()).squaredNorm(); } } // compute weight (stiffness) double max_energy = *std::max_element(tetEnergy.begin(), tetEnergy.end()); for(int i=0;i<mesh.numTet;i++) { double w = 1.0 - tetEnergy[i]/(max_energy+EPSILON); mesh.tetWeight[i] *= w*w; } } // find constraint points constraint.resize(3*numPrb); for(int i=0;i<numPrb;i++){ constraint[3*i] = T(i,mesh.tetList[4*D.closestTet[i]],constraintWeight); constraint[3*i+1] = T(i,mesh.tetList[4*D.closestTet[i]+1],constraintWeight); constraint[3*i+2] = T(i,mesh.tetList[4*D.closestTet[i]+2],constraintWeight); } if( constraintMode == CONSTRAINT_NEIGHBOUR ){ std::vector<double> probeConstraintRadius(numPrb); MArrayDataHandle handle = data.inputArrayValue(aProbeConstraintRadius); if(handle.elementCount() != numPrb){ MGlobal::displayInfo("# of Probes and probeConstraintRadius are different"); return MS::kFailure; } for(int i=0;i<numPrb;i++){ handle.jumpToArrayElement(i); probeConstraintRadius[i]=handle.inputValue().asDouble(); } double constraintRadius = data.inputValue( aConstraintRadius ).asDouble(); for(int i=0;i<numPrb;i++){ double r = constraintRadius * probeConstraintRadius[i]; for(int j=0;j<numPts;j++){ if(D.distPts[i][j]<r){ constraint.push_back(T(i,j,constraintWeight * pow((r-D.distPts[i][j])/r,normExponent))); } } } } int numConstraint=constraint.size(); mesh.constraintWeight.resize(numConstraint); mesh.constraintVal.resize(numConstraint,numPrb); for(int cur=0;cur<numConstraint;cur++){ mesh.constraintWeight[cur] = std::make_pair(constraint[cur].col(), constraint[cur].value()); } // isError = mesh.ARAPprecompute(); status = data.setClean(aARAP); } // END of ARAP precomputation if(isError>0){ return MS::kFailure; } // probe weight computation if(!data.isClean(aComputeWeight) || isNumProbeChanged){ // load probe weights MArrayDataHandle handle = data.inputArrayValue(aProbeWeight); if(handle.elementCount() != numPrb){ MGlobal::displayInfo("# of Probes and probeWeight are different"); isError = ERROR_ATTR; return MS::kFailure; } double effectRadius = data.inputValue( aEffectRadius ).asDouble(); std::vector<double> probeWeight(numPrb), probeRadius(numPrb); for(int i=0;i<numPrb;i++){ handle.jumpToArrayElement(i); probeWeight[i] = handle.inputValue().asDouble(); probeRadius[i] = probeWeight[i] * effectRadius; } wr.resize(mesh.numTet);ws.resize(mesh.numTet);wl.resize(mesh.numTet); for(int j=0;j<mesh.numTet;j++){ wr[j].resize(numPrb); ws[j].resize(numPrb); wl[j].resize(numPrb); } short weightMode = data.inputValue( aWeightMode ).asShort(); if (weightMode == WM_INV_DISTANCE){ for(int j=0;j<mesh.numTet;j++){ double sum=0.0; std::vector<double> idist(numPrb); for (int i = 0; i<numPrb; i++){ idist[i] = probeRadius[i] / pow(D.distTet[i][j], normExponent); sum += idist[i]; } for (int i = 0; i<numPrb; i++){ wr[j][i] = ws[j][i] = wl[j][i] = sum > 0 ? idist[i] / sum : 0.0; } } } else if (weightMode == WM_CUTOFF_DISTANCE){ for(int j=0;j<mesh.numTet;j++){ for (int i = 0; i<numPrb; i++){ wr[j][i] = ws[j][i] = wl[j][i] = (D.distTet[i][j] > probeRadius[i]) ? 0 : pow((probeRadius[i] - D.distTet[i][j]) / probeRadius[i], normExponent); } } }else if (weightMode == WM_DRAW){ float val; MRampAttribute rWeightCurveR( thisNode, aWeightCurveR, &status ); MRampAttribute rWeightCurveS( thisNode, aWeightCurveS, &status ); MRampAttribute rWeightCurveL( thisNode, aWeightCurveL, &status ); for(int j=0;j<mesh.numTet;j++){ for (int i = 0; i < numPrb; i++){ rWeightCurveR.getValueAtPosition(D.distTet[i][j] / probeRadius[i], val); wr[j][i] = val; rWeightCurveS.getValueAtPosition(D.distTet[i][j] / probeRadius[i], val); ws[j][i] = val; rWeightCurveL.getValueAtPosition(D.distTet[i][j] / probeRadius[i], val); wl[j][i] = val; } } }else if(weightMode & WM_HARMONIC){ Laplacian harmonicWeighting; makeFaceTet(data, input, inputGeom, mIndex, pts, harmonicWeighting.tetList, harmonicWeighting.tetMatrix, harmonicWeighting.tetWeight); harmonicWeighting.numTet = (int)harmonicWeighting.tetList.size()/4; std::vector<T> weightConstraint(numPrb); // the vertex closest to the probe is given probeWeight for(int i=0;i<numPrb;i++){ weightConstraint[i]=T(i,D.closestPts[i],probeWeight[i]); } // vertices within effectRadius are given probeWeight if( data.inputValue( aNeighbourWeighting ).asBool() ){ for(int i=0;i<numPrb;i++){ for(int j=0;j<numPts;j++){ if(D.distPts[i][j]<probeRadius[i]){ weightConstraint.push_back(T(i,j,probeWeight[i])); } } } } // set boundary condition for weight computation int numConstraint=weightConstraint.size(); harmonicWeighting.constraintWeight.resize(numConstraint); harmonicWeighting.constraintVal.resize(numConstraint,numPrb); harmonicWeighting.constraintVal.setZero(); for(int i=0;i<numConstraint;i++){ harmonicWeighting.constraintVal(i,weightConstraint[i].row())=weightConstraint[i].value(); harmonicWeighting.constraintWeight[i] = std::make_pair(weightConstraint[i].col(), weightConstraint[i].value()); } // clear tetWeight if(!areaWeighted){ harmonicWeighting.tetWeight.clear(); harmonicWeighting.tetWeight.resize(harmonicWeighting.numTet,1.0); } // solve the laplace equation if( weightMode == WM_HARMONIC_ARAP){ harmonicWeighting.computeTetMatrixInverse(); harmonicWeighting.dim = numPts + harmonicWeighting.numTet; isError = harmonicWeighting.ARAPprecompute(); }else if(weightMode == WM_HARMONIC_COTAN){ harmonicWeighting.dim = numPts; isError = harmonicWeighting.cotanPrecompute(); } if(isError>0) return MS::kFailure; std::vector< std::vector<double> > w_tet(numPrb); harmonicWeighting.harmonicSolve(); for(int i=0;i<numPrb;i++){ makeTetWeightList(tetMode, mesh.tetList, faceList, edgeList, vertexList, harmonicWeighting.Sol.col(i), w_tet[i]); for(int j=0;j<mesh.numTet; j++){ wr[j][i] = ws[j][i] = wl[j][i] = w_tet[i][j]; } } } // normalise weights short normaliseWeightMode = data.inputValue( aNormaliseWeight ).asShort(); for(int j=0;j<mesh.numTet;j++){ D.normaliseWeight(normaliseWeightMode, wr[j]); D.normaliseWeight(normaliseWeightMode, ws[j]); D.normaliseWeight(normaliseWeightMode, wl[j]); } status = data.setClean(aComputeWeight); } // END of weight computation // setting up transformation matrix B.rotationConsistency = data.inputValue( aRotationConsistency ).asBool(); bool frechetSum = data.inputValue( aFrechetSum ).asBool(); blendedSE.resize(mesh.numTet); blendedR.resize(mesh.numTet); blendedS.resize(mesh.numTet); blendedL.resize(mesh.numTet);A.resize(mesh.numTet); for(int i=0;i<numPrb;i++){ B.Aff[i]=initMatrix[i].inverse()*matrix[i]; } B.parametrise(blendMode); // prepare transform matrix for each simplex #pragma omp parallel for for (int j = 0; j < mesh.numTet; j++){ // blend matrix if (blendMode == BM_SRL){ blendedS[j] = expSym(blendMat(B.logS, ws[j])); Vector3d l = blendMat(B.L, wl[j]); blendedR[j] = frechetSum ? frechetSO(B.R, wr[j]) : expSO(blendMat(B.logR, wr[j])); A[j] = pad(blendedS[j]*blendedR[j], l); } else if (blendMode == BM_SSE){ blendedS[j] = expSym(blendMat(B.logS, ws[j])); blendedSE[j] = expSE(blendMat(B.logSE, wr[j])); A[j] = pad(blendedS[j], Vector3d::Zero()) * blendedSE[j]; } else if (blendMode == BM_LOG3){ blendedR[j] = blendMat(B.logGL, wr[j]).exp(); Vector3d l = blendMat(B.L, wl[j]); A[j] = pad(blendedR[j], l); } else if (blendMode == BM_LOG4){ A[j] = blendMat(B.logAff, wr[j]).exp(); } else if (blendMode == BM_SQL){ Vector4d q = blendQuat(B.quat, wr[j]); Vector3d l = blendMat(B.L, wl[j]); blendedS[j] = blendMatLin(B.S, ws[j]); Quaternion<double> RQ(q); blendedR[j] = RQ.matrix().transpose(); A[j] = pad(blendedS[j]*blendedR[j], l); } else if (blendMode == BM_AFF){ A[j] = blendMatLin(B.Aff, wr[j]); } } // compute target vertices position tetEnergy.resize(mesh.numTet); // set constraint int numConstraints = constraint.size(); mesh.constraintVal.resize(numConstraints,3); RowVector4d cv; for(int cur=0;cur<numConstraints;cur++){ cv = pad(pts[constraint[cur].col()]) * B.Aff[constraint[cur].row()]; mesh.constraintVal(cur,0) = cv[0]; mesh.constraintVal(cur,1) = cv[1]; mesh.constraintVal(cur,2) = cv[2]; } // iterate to determine vertices position for(int k=0;k<numIter;k++){ // solve ARAP mesh.ARAPSolve(A); // set new vertices position new_pts.resize(numPts); for(int i=0;i<numPts;i++){ new_pts[i][0]=mesh.Sol(i,0); new_pts[i][1]=mesh.Sol(i,1); new_pts[i][2]=mesh.Sol(i,2); } // if iteration continues if(k+1<numIter || visualisationMode == VM_ENERGY){ std::vector<double> dummy_weight; makeTetMatrix(tetMode, new_pts, mesh.tetList, faceList, edgeList, vertexList, Q, dummy_weight); Matrix3d S,R,newS,newR; if(blendMode == BM_AFF || blendMode == BM_LOG4 || blendMode == BM_LOG3){ for(int i=0;i<mesh.numTet;i++){ polarHigham(A[i].block(0,0,3,3), blendedS[i], blendedR[i]); } } #pragma omp parallel for for(int i=0;i<mesh.numTet;i++){ polarHigham((mesh.tetMatrixInverse[i]*Q[i]).block(0,0,3,3), newS, newR); tetEnergy[i] = (newS-blendedS[i]).squaredNorm(); A[i].block(0,0,3,3) = blendedS[i]*newR; // polarHigham((A[i].transpose()*PI[i]*Q[i]).block(0,0,3,3), newS, newR); // A[i].block(0,0,3,3) *= newR; } } } for(int i=0;i<numPts;i++){ Mpts[i].x=mesh.Sol(i,0); Mpts[i].y=mesh.Sol(i,1); Mpts[i].z=mesh.Sol(i,2); } if(worldMode){ for(int i=0;i<numPts;i++) Mpts[i] *= localToWorldMatrix.inverse(); } itGeo.setAllPositions(Mpts); // set vertex colour if(visualisationMode != VM_OFF){ std::vector<double> ptsColour(numPts, 0.0); if(visualisationMode == VM_ENERGY){ makePtsWeightList(tetMode, numPts, mesh.tetList, faceList, edgeList, vertexList, tetEnergy, ptsColour); for(int i=0;i<numPts;i++){ ptsColour[i] *= visualisationMultiplier; } }else if(visualisationMode == VM_STIFFNESS){ makePtsWeightList(tetMode, numPts, mesh.tetList, faceList, edgeList, vertexList, mesh.tetWeight, ptsColour); double maxval = *std::max_element(ptsColour.begin(), ptsColour.end()); for(int i=0;i<numPts;i++){ ptsColour[i] = 1.0 - ptsColour[i]/maxval; } }else if(visualisationMode == VM_CONSTRAINT){ for(int i=0;i<constraint.size();i++){ ptsColour[constraint[i].col()] += constraint[i].value(); } }else if(visualisationMode == VM_EFFECT){ std:vector<double> wsum(mesh.numTet); for(int j=0;j<mesh.numTet;j++){ //wsum[j] = std::accumulate(wr[j].begin(), wr[j].end(), 0.0); wsum[j]= visualisationMultiplier * wr[j][numPrb-1]; } makePtsWeightList(tetMode, numPts, mesh.tetList, faceList, edgeList, vertexList, wsum, ptsColour); } visualise(data, outputGeom, ptsColour); } return MS::kSuccess; }
MStatus HRBFSkinCluster::skinDQ(MMatrixArray& transforms, int numTransforms, MArrayDataHandle& weightListHandle, MItGeometry& iter) { MStatus returnStatus; // compute dual quaternions. we're storing them as a parallel array. std::vector<MQuaternion> tQuaternions(numTransforms); // translation quaterions std::vector<MQuaternion> rQuaternions(numTransforms); // rotation quaternions for (int i = 0; i < numTransforms; i++) { rQuaternions.at(i) = getRotationQuaternion(transforms[i]); rQuaternions.at(i).normalizeIt(); tQuaternions.at(i) = getTranslationQuaternion(transforms[i], rQuaternions.at(i)); #if DEBUG_PRINTS std::cout << "rota quaternion " << i << " is: " << rQuaternions.at(i) << std::endl; std::cout << "tran quaternion " << i << " is: " << tQuaternions.at(i) << std::endl; #endif } MQuaternion rBlend; // blended rotation quaternions MQuaternion tBlend; // blended translation quaternions MQuaternion scaleMe; // Maya's quaternion scaling is in-place double weight; // Iterate through each point in the geometry. // for (; !iter.isDone(); iter.next()) { MPoint pt = iter.position(); MPoint skinned; rBlend = MQuaternion(); // reset tBlend = MQuaternion(); // reset rBlend[3] = 0.0; tBlend[3] = 0.0; // get the weights for this point MArrayDataHandle weightsHandle = weightListHandle.inputValue().child(weights); // compute the skinning for (int i = 0; i<numTransforms; ++i) { if (MS::kSuccess == weightsHandle.jumpToElement(i)) { weight = weightsHandle.inputValue().asDouble(); scaleMe = rQuaternions.at(i); rBlend = rBlend + scaleMe.scaleIt(weight); scaleMe = tQuaternions.at(i); tBlend = tBlend + scaleMe.scaleIt(weight); } } MMatrix dqMatrix = makeDQMatrix(rBlend.normalizeIt(), tBlend); skinned = pt * dqMatrix; // Set the final position. iter.setPosition(skinned); // advance the weight list handle weightListHandle.next(); } return returnStatus; }
MObject fullLoft::loft( MArrayDataHandle &inputArray, MObject &newSurfData, MStatus &stat ) { MFnNurbsSurface surfFn; MPointArray cvs; MDoubleArray ku, kv; int i, j; int numCVs; int numCurves = inputArray.elementCount (); // Ensure that we have at least 1 element in the input array // We must not do an inputValue on an element that does not // exist. if ( numCurves < 1 ) return MObject::kNullObj; // Count the number of CVs inputArray.jumpToElement(0); MDataHandle elementHandle = inputArray.inputValue(&stat); if (!stat) { stat.perror("fullLoft::loft: inputValue"); return MObject::kNullObj; } MObject countCurve (elementHandle.asNurbsCurve()); MFnNurbsCurve countCurveFn (countCurve); numCVs = countCurveFn.numCVs (&stat); PERRORnull("fullLoft::loft counting CVs"); // Create knot vectors for U and V // U dimension contains one CV from each curve, triple knotted for (i = 0; i < numCurves; i++) { ku.append (double (i)); ku.append (double (i)); ku.append (double (i)); } // V dimension contains all of the CVs from one curve, triple knotted at // the ends kv.append( 0.0 ); kv.append( 0.0 ); kv.append( 0.0 ); for ( i = 1; i < numCVs - 3; i ++ ) kv.append( (double) i ); kv.append( numCVs-3 ); kv.append( numCVs-3 ); kv.append( numCVs-3 ); // Build the surface's CV array for (int curveNum = 0; curveNum < numCurves; curveNum++) { MObject curve (inputArray.inputValue ().asNurbsCurve ()); MFnNurbsCurve curveFn (curve); MPointArray curveCVs; stat = curveFn.getCVs (curveCVs, MSpace::kWorld); PERRORnull("fullLoft::loft getting CVs"); if (curveCVs.length() != (unsigned)numCVs) stat = MS::kFailure; PERRORnull("fullLoft::loft inconsistent number of CVs - rebuild curves"); // Triple knot for every curve but the first int repeats = (curveNum == 0) ? 1 : 3; for (j = 0; j < repeats; j++) for ( i = 0; i < numCVs; i++ ) cvs.append (curveCVs [i]); stat = inputArray.next (); } MObject surf = surfFn.create(cvs, ku, kv, 3, 3, MFnNurbsSurface::kOpen, MFnNurbsSurface::kOpen, false, newSurfData, &stat ); PERRORnull ("fullLoft::Loft create surface"); return surf; }
MStatus snapDeformer::deform(MDataBlock &data, MItGeometry &iter, const MMatrix &mat, unsigned int multiIndex) { MStatus stat; //lets see if we need to do anything MDataHandle DataHandle = data.inputValue(envelope, &stat); float env = DataHandle.asFloat(); if (env == 0) return stat; DataHandle = data.inputValue(weight, &stat); const float weight = DataHandle.asFloat(); if (weight == 0) return stat; env = (env*weight); //space target DataHandle = data.inputValue(space, &stat); int SpaceInt = DataHandle.asInt(); //space source DataHandle = data.inputValue(spaceSource, &stat); int SpaceSourceInt = DataHandle.asInt(); //pointlist MArrayDataHandle pointArrayHandle = data.inputArrayValue(pointList); //snapMesh MFnMesh SnapMesh; DataHandle = data.inputValue(snapMesh, &stat); if (!stat) return Err(stat,"Can't get mesh to snap to"); MObject SnapMeshObj = DataHandle.asMesh(); SnapMesh.setObject(SnapMeshObj); MPointArray snapPoints; if (SpaceSourceInt==0) SnapMesh.getPoints(snapPoints, MSpace::kWorld); else SnapMesh.getPoints(snapPoints, MSpace::kObject); iter.reset(); for ( ; !iter.isDone(); iter.next()) { //check for painted weights float currEnv = env * weightValue(data, multiIndex, iter.index()); //get point to snap to unsigned int index; stat = pointArrayHandle.jumpToElement(iter.index()); if (!stat) index = 0; else { DataHandle = pointArrayHandle.outputValue(); index = DataHandle.asInt(); } if (index != -1) { //calc point location MPoint currPoint; if (snapPoints.length() > index) currPoint = snapPoints[index]; if (SpaceInt == 0) currPoint *= mat.inverse(); if (currEnv !=1) { MPoint p = (currPoint- iter.position()); currPoint = iter.position() + (p*currEnv); } //set point location iter.setPosition(currPoint); } } return stat; }
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; }
MStatus stringFormat::compute (const MPlug& plug, MDataBlock& data) { MStatus status; // Check that the requested recompute is one of the output values // if (plug == attrOutput) { // Read the input values // MDataHandle inputData = data.inputValue (attrFormat, &status); CHECK_MSTATUS( status ); MString format = inputData.asString(); // Get input data handle, use outputArrayValue since we do not // want to evaluate all inputs, only the ones related to the // requested multiIndex. This is for efficiency reasons. // MArrayDataHandle vals = data.outputArrayValue(attrValues, &status); CHECK_MSTATUS( status ); int indx = 0; int param; char letter; while ((indx = findNextMatch(format, indx, param, letter)) > 0) { double val = 0.; status = vals.jumpToElement(param); if (status == MStatus::kSuccess) { MDataHandle thisVal = vals.inputValue( &status ); if (status == MStatus::kSuccess) { val = thisVal.asDouble(); } } MString replace; bool valid = false; switch (letter) { case 'd': // Integer val = floor(val+.5); // No break here case 'f': // Float replace.set(val); valid = true; break; case 't': // Timecode { const char * sign = ""; if (val<0) { sign = "-"; val = -val; } int valInt = (int)(val+.5); int sec = valInt / 24; int frame = valInt - sec * 24; int min = sec / 60; sec -= min * 60; int hour = min / 60; min -= hour * 60; char buffer[90]; if (hour>0) sprintf(buffer, "%s%d:%02d:%02d.%02d", sign, hour, min, sec, frame); else sprintf(buffer, "%s%02d:%02d.%02d", sign, min, sec, frame); replace = buffer; } valid = true; break; } if (valid) { format = format.substring(0, indx-2) + replace + format.substring(indx+2, format.length()-1); indx += replace.length() - 3; } } // Store the result // MDataHandle output = data.outputValue(attrOutput, &status ); CHECK_MSTATUS( status ); output.set( format ); } else { return MS::kUnknownParameter; } return MS::kSuccess; }
MStatus clusterControledCurve::compute( const MPlug& plug, MDataBlock& data ) { //MFnDependencyNode thisNode( thisMObject() ); //cout << thisNode.name() << ", start" << endl; MStatus status; MDataHandle hInputCurve = data.inputValue( aInputCurve, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MDataHandle hInputCurveMatrix = data.inputValue( aInputCurveMatrix, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MDataHandle hOutputCurve = data.outputValue( aOutputCurve, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MArrayDataHandle hArrBindPreMatrix = data.inputArrayValue( aBindPreMatrix, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MArrayDataHandle hArrMatrix = data.inputArrayValue( aMatrix, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MArrayDataHandle hArrWeightList = data.inputArrayValue( aWeightList, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MDataHandle hUpdate = data.inputValue( aUpdate, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); MObject oInputCurve = hInputCurve.asNurbsCurve(); int bindPreMatrixLength = hArrBindPreMatrix.elementCount(); int matrixLength = hArrMatrix.elementCount(); MFnNurbsCurve fnInputCurve = oInputCurve; int numCVs = fnInputCurve.numCVs(); int weightListLength = hArrWeightList.elementCount(); if( weightListLength > 100 ) { cout << "WeightList Count Error : " << weightListLength << endl; return MS::kFailure; } MPointArray inputCvPoints; MPointArray outputCvPoints; fnInputCurve.getCVs( inputCvPoints ); outputCvPoints.setLength( numCVs ); MMatrix matrix; MMatrix inputCurveMatrix = hInputCurveMatrix.asMatrix(); MMatrix inputCurveMatrixInverse = inputCurveMatrix.inverse(); if( requireUpdate ) CHECK_MSTATUS_AND_RETURN_IT( updateBindPreMatrix( oInputCurve, inputCurveMatrixInverse, hArrMatrix, hArrBindPreMatrix, hUpdate.asBool() ) ); for( int i=0; i< numCVs; i++ ) { inputCvPoints[i] *= inputCurveMatrix; } for( int i=0; i< numCVs; i++ ) { outputCvPoints[i] = MPoint( 0,0,0 ); double weight; for( int j=0; j< matrixLength; j++ ) { weight = setWeights[i][j]; hArrMatrix.jumpToElement( j ); matrix = hArrMatrix.inputValue().asMatrix(); outputCvPoints[i] += inputCvPoints[i]*bindPreMatrix[j]*matrix*weight; } } for( int i=0; i< numCVs; i++ ) { outputCvPoints[i] *= inputCurveMatrixInverse; } MFnNurbsCurveData outputCurveData; MObject oOutputCurve = outputCurveData.create(); fnInputCurve.copy( oInputCurve, oOutputCurve ); MFnNurbsCurve fnOutputCurve( oOutputCurve, &status ); CHECK_MSTATUS_AND_RETURN_IT( status ); fnOutputCurve.setCVs( outputCvPoints ); hOutputCurve.set( oOutputCurve ); data.setClean( plug ); //cout << thisNode.name() << ", end" << endl; return status; }
MStatus HesMeshNode::compute( const MPlug& plug, MDataBlock& data ) { MStatus stat; MPlug pnames(thisMObject(), ameshname); const unsigned numMeshes = pnames.numElements(); MString cacheName = data.inputValue( input ).asString(); std::string substitutedCacheName(cacheName.asChar()); EnvVar::replace(substitutedCacheName); MArrayDataHandle meshNameArray = data.inputArrayValue( ameshname ); MArrayDataHandle meshArry = data.outputArrayValue(outMesh, &stat); bool hesStat = false; if( plug.array() == outMesh ) { const unsigned idx = plug.logicalIndex(); if(BaseUtil::IsImporting) hesStat = true; else { if(idx == 0) AHelper::Info<std::string>(" hes mesh open file ", substitutedCacheName ); hesStat = BaseUtil::OpenHes(substitutedCacheName, HDocument::oReadOnly); } if(!hesStat) { AHelper::Info<std::string >("hes mesh cannot open file ", substitutedCacheName); return MS::kFailure; } meshNameArray.jumpToElement(idx); const MString meshName = meshNameArray.inputValue().asString(); if(!BaseUtil::HesDoc->find(meshName.asChar())) { AHelper::Info<MString>(" hes cannot find mesh ", meshName ); return MS::kFailure; } meshArry.jumpToElement(idx); MDataHandle hmesh = meshArry.outputValue(); HPolygonalMesh entryMesh(meshName.asChar() ); APolygonalMesh dataMesh; entryMesh.load(&dataMesh); entryMesh.close(); MFnMeshData dataCreator; MObject outMeshData = dataCreator.create(&stat); if( !stat ) { MGlobal::displayWarning("hes mesh cannot create " + meshName); return MS::kFailure; } AHelper::Info<MString>(" hes init mesh ", meshName); HesperisPolygonalMeshCreator::create(&dataMesh, outMeshData); hmesh.set(outMeshData); data.setClean(plug); if( (idx+1)>=numMeshes ) { if(!BaseUtil::IsImporting) { AHelper::Info<std::string>(" hes mesh close file ", substitutedCacheName ); BaseUtil::CloseHes(); } } } else { return MS::kUnknownParameter; } return MS::kSuccess; }
// COMPUTE ====================================== MStatus gear_rollSplineKine::compute(const MPlug& plug, MDataBlock& data) { MStatus returnStatus; // Error check if (plug != output) return MS::kUnknownParameter; // Get inputs matrices ------------------------------ // Inputs Parent MArrayDataHandle adh = data.inputArrayValue( ctlParent ); int count = adh.elementCount(); if (count < 1) return MS::kFailure; MMatrixArray inputsP(count); for (int i = 0 ; i < count ; i++){ adh.jumpToElement(i); inputsP[i] = adh.inputValue().asMatrix(); } // Inputs adh = data.inputArrayValue( inputs ); if (count != adh.elementCount()) return MS::kFailure; MMatrixArray inputs(count); for (int i = 0 ; i < count ; i++){ adh.jumpToElement(i); inputs[i] = adh.inputValue().asMatrix(); } adh = data.inputArrayValue( inputsRoll ); if (count != adh.elementCount()) return MS::kFailure; MDoubleArray roll(adh.elementCount()); for (int i = 0 ; i < count ; i++){ adh.jumpToElement(i); roll[i] = degrees2radians((double)adh.inputValue().asFloat()); } // Output Parent MDataHandle ha = data.inputValue( outputParent ); MMatrix outputParent = ha.asMatrix(); // Get inputs sliders ------------------------------- double in_u = (double)data.inputValue( u ).asFloat(); bool in_resample = data.inputValue( resample ).asBool(); int in_subdiv = data.inputValue( subdiv ).asShort(); bool in_absolute = data.inputValue( absolute ).asBool(); // Process ------------------------------------------ // Get roll, pos, tan, rot, scl MVectorArray pos(count); MVectorArray tan(count); MQuaternion *rot; rot = new MQuaternion[count]; MVectorArray scl(count); double threeDoubles[3]; for (int i = 0 ; i < count ; i++){ MTransformationMatrix tp(inputsP[i]); MTransformationMatrix t(inputs[i]); pos[i] = t.getTranslation(MSpace::kWorld); rot[i] = tp.rotation(); t.getScale(threeDoubles, MSpace::kWorld); scl[i] = MVector(threeDoubles[0], threeDoubles[1], threeDoubles[2]); tan[i] = MVector(threeDoubles[0] * 2.5, 0, 0).rotateBy(t.rotation()); } // Get step and indexes // We define between wich controlers the object is to be able to // calculate the bezier 4 points front this 2 objects double step = 1.0 / max( 1, count-1.0 ); int index1 = (int)min( count-2.0, in_u/step ); int index2 = index1+1; int index1temp = index1; int index2temp = index2; double v = (in_u - step * double(index1)) / step; double vtemp = v; // calculate the bezier MVector bezierPos; MVector xAxis, yAxis, zAxis; if(!in_resample){ // straight bezier solve MVectorArray results = bezier4point(pos[index1],tan[index1],pos[index2],tan[index2],v); bezierPos = results[0]; xAxis = results[1]; } else if(!in_absolute){ MVectorArray presample(in_subdiv); MVectorArray presampletan(in_subdiv); MDoubleArray samplelen(in_subdiv); double samplestep = 1.0 / double(in_subdiv-1); double sampleu = samplestep; presample[0] = pos[index1]; presampletan[0] = tan[index1]; MVector prevsample(presample[0]); MVector diff; samplelen[0] = 0; double overalllen = 0; MVectorArray results(2); for(long i=1;i<in_subdiv;i++,sampleu+=samplestep){ results = bezier4point(pos[index1],tan[index1],pos[index2],tan[index2],sampleu); presample[i] = results[0]; presampletan[i] = results[1]; diff = presample[i] - prevsample; overalllen += diff.length(); samplelen[i] = overalllen; prevsample = presample[i]; } // now as we have the sampleu = 0; for(long i=0;i<in_subdiv-1;i++,sampleu+=samplestep){ samplelen[i+1] = samplelen[i+1] / overalllen; if(v>=samplelen[i] && v <= samplelen[i+1]){ v = (v - samplelen[i]) / (samplelen[i+1] - samplelen[i]); bezierPos = linearInterpolate(presample[i],presample[i+1],v); xAxis = linearInterpolate(presampletan[i],presampletan[i+1],v); break; } } } else{ MVectorArray presample(in_subdiv); MVectorArray presampletan(in_subdiv); MDoubleArray samplelen(in_subdiv); double samplestep = 1.0 / double(in_subdiv-1); double sampleu = samplestep; presample[0] = pos[0]; presampletan[0] = tan[0]; MVector prevsample(presample[0]); MVector diff; samplelen[0] = 0; double overalllen = 0; MVectorArray results; for(long i=1;i<in_subdiv;i++,sampleu+=samplestep){ index1 = (int)min(count-2,sampleu / step); index2 = index1+1; v = (sampleu - step * double(index1)) / step; results = bezier4point(pos[index1],tan[index1],pos[index2],tan[index2],v); presample[i] = results[0]; presampletan[i] = results[1]; diff = presample[i] - prevsample; overalllen += diff.length(); samplelen[i] = overalllen; prevsample = presample[i]; } // now as we have the sampleu = 0; for(long i=0;i<in_subdiv-1;i++,sampleu+=samplestep){ samplelen[i+1] = samplelen[i+1] / overalllen; if(in_u>=samplelen[i] && in_u <= samplelen[i+1]){ in_u = (in_u - samplelen[i]) / (samplelen[i+1] - samplelen[i]); bezierPos = linearInterpolate(presample[i],presample[i+1],in_u); xAxis = linearInterpolate(presampletan[i],presampletan[i+1],in_u); break; } } } // compute the scaling (straight interpolation!) MVector scl1 = linearInterpolate(scl[index1temp], scl[index2temp],vtemp); // compute the rotation! MQuaternion q = slerp(rot[index1temp], rot[index2temp], vtemp); yAxis = MVector(0,1,0); yAxis = yAxis.rotateBy(q); // use directly or project the roll values! // print roll double a = linearInterpolate(roll[index1temp], roll[index2temp], vtemp); yAxis = yAxis.rotateBy( MQuaternion(xAxis.x * sin(a/2.0), xAxis.y * sin(a/2.0), xAxis.z * sin(a/2.0), cos(a/2.0))); zAxis = xAxis ^ yAxis; zAxis.normalize(); yAxis = zAxis ^ xAxis; yAxis.normalize(); // Output ------------------------------------------- MTransformationMatrix result; // translation result.setTranslation(bezierPos, MSpace::kWorld); // rotation q = getQuaternionFromAxes(xAxis,yAxis,zAxis); result.setRotationQuaternion(q.x, q.y, q.z, q.w); // scaling threeDoubles[0] = 1; threeDoubles[0] = scl1.y; threeDoubles[0] = scl1.z; result.setScale(threeDoubles, MSpace::kWorld); MDataHandle h = data.outputValue( output ); h.setMMatrix( result.asMatrix() * outputParent.inverse() ); data.setClean( plug ); return MS::kSuccess; }