def testDdm(): # test for skin mesh = cmds.polyCylinder(r=1, h=10, sx=8, sy=40, sz=8, ax=(0, 0, 1), ch=0)[0] # skin cluster to transfer base weights skcMesh = cmds.duplicate(mesh, n="skcMesh")[0] refMesh = cmds.duplicate(mesh, n="refDdmMesh")[0] joints = [] refJoints = [] for i in range(3): jnt = cmds.createNode("joint", n="mainJnt_{}".format(i)) cmds.setAttr(jnt + ".translateZ", i * 5 - 5) joints.append(jnt) if i: cmds.parent(jnt, joints[i - 1]) skin = cmds.skinCluster(joints, skcMesh)[0] ddm = cmds.deformer(mesh, type="directDeltaMush")[0] refDdm = cmds.deformer(refMesh, type="refDDM")[0] for mush in [ddm, refDdm]: # copy matrix connections for i in range(3): cmds.connectAttr(joints[i] + ".worldMatrix[0]", mush + ".matrix[{}]".format(i)) #cmds.connectAttr(joints[i] + ".worldMatrix[0]", refDdm + ".matrix[{}]".format(i)) pass # copy plug connections and weights to deltamush copyPlugs = ("weightList", "bindPreMatrix") size = cmds.getAttr(skin + ".weightList", size=1) cmds.setAttr(mush + ".weightList", size=size) cmds.select(cl=1) for i in copyPlugs: sourcePlug = getMPlug(getMObject(skin), i) sourceDH = om.MArrayDataHandle(sourcePlug.asMDataHandle()) sinkPlug = getMPlug(getMObject(mush), i) sinkDH = om.MArrayDataHandle(sinkPlug.asMDataHandle()) sinkDH.copy(sourceDH) sourceDH = sourcePlug.asMDataHandle() sinkPlug.setMDataHandle(om.MDataHandle(sourceDH)) cmds.select(mush) # direct weight connections from skincluster to allow live weight editing for i in range(cmds.getAttr(skin + ".weightList", size=1)): array = skin + ".weightList[{}]".format(i) # for n in range( cmds.getAttr(array + ".weights", size=1 )): # srcPlug = array + ".weights[{}]".format(n) srcPlug = array dstPlug = srcPlug.replace(skin, mush) cmds.connectAttr(srcPlug, dstPlug, f=1) cmds.setAttr(ddm + ".iterations", 10) cmds.setAttr(ddm + ".alpha", 0.5) cmds.setAttr(ddm + ".smoothTranslation", 10.0) cmds.setAttr(ddm + ".smoothRotation", 10.0) # move skc mesh off to side group = cmds.group(skcMesh, n="skcOffsetGrp") cmds.setAttr(group + ".translateX", 5) group = cmds.group(refMesh, n="refOffsetGrp") cmds.setAttr(group + ".translateX", -5)
def compute(self, pPlug, pData): #only compute if output is in out array if (pPlug.parent() == generalIk.aOutRot): # descend into coordinated cycles # inputs solverDH = pData.inputValue(generalIk.aSolver) solver = solverDH.asInt() iterationsDH = pData.inputValue(generalIk.aMaxIter) maxIter = iterationsDH.asInt() toleranceDH = pData.inputValue(generalIk.aTolerance) tolerance = toleranceDH.asFloat() rootMat = om.MMatrix() rootMatDH = pData.inputValue(generalIk.aRootMat) rootMat = rootMatDH.asMatrix() # target targetMat = om.MMatrix() targetMatDH = pData.inputValue(generalIk.aTargetMat) targetMat = targetMatDH.asMatrix() endMat = om.MMatrix() endMatDH = pData.inputValue(generalIk.aEndMat) endMat = endMatDH.asMatrix() print "endMat at start is {}".format(endMat) # get everything in root space targetRSmat = targetMat.__mul__(rootMat.inverse()) # get reference matrix array from tip to root # this lets us virtually rebuild the parent-child hierarchy inJntArrayDH = pData.inputArrayValue(generalIk.aJnts) inLength = inJntArrayDH.__len__() # DON'T FORGET our useful arrays are one entry shorter than we have chainArray = om.MMatrixArray() jntArray = om.MMatrixArray() jntWeights = om.MFloatArray() for i in range(inLength): inJntArrayDH.jumpToPhysicalElement(i) childCompDH = inJntArrayDH.inputValue() childMat = om.MMatrix() childMatDH = om.MDataHandle( childCompDH.child(generalIk.aJntMat)) #childUpMatDH = om.MDataHandle(childCompDH.child(generalIk.aJntUpMat)) # not needed atm childMat = childMatDH.asMatrix() print "joint{}mat is {}".format(i, childMat) parentMat = om.MMatrix() if i != 0: inJntArrayDH.jumpToPhysicalElement(i - 1) parentCompDH = inJntArrayDH.inputValue() parentMatDH = om.MDataHandle( parentCompDH.child(generalIk.aJntMat)) parentMat = parentMatDH.asMatrix() else: # only applies to last in loop (so first in joints) parentMat = rootMat chainMat = childMat.__mul__(parentMat.inverse()) chainArray.append(chainMat) #chainArray is every joint in the space of its parent #chainArray[i] = jntMat[i] x jntMat[i-1].inverse jntArray.append(childMat) #childArray is just the joint matrices childWeightDH = om.MDataHandle( childCompDH.child(generalIk.aJntWeight)) childWeight = childWeightDH.asFloat() print "childWeight is {}".format(childWeight) jntWeights.append(childWeight) print "" #from here applies only to ccd algorithm gap = 1.0 iter = 0 while (iter < maxIter) and (gap > tolerance): print "ITERATION {}".format(iter) print "" for i in range(inLength): print "computing joint {} of {}".format(i, inLength) # welcome to the bone zone # reconstruct hierarchy with matrices from previous iteration # currently target is known in rootspace, and end isn't known at all # backwards to get target and forwards to get end, both in active joint space # # +(target) # . # . # O # / \ X(end) # / \ / # / O # / #(root) # this works by getting vector from active joint to end, from active joint to target, # then aligning one to the other. joints are assumed to have direct parent-child # hierarchy, so all rotations are inherited rigidly # ccd operates from tip to root - see reference for actual algorithm jntMat = om.MMatrix() jntMat = jntArray.__getitem__(inLength - (i + 1)) print "jntMat is {}".format(jntMat) chainMult = om.MMatrix() #backwards for b in range(inLength - i): #multiply targetRootSpaceMat by inverse chainMats, #starting from ROOT print "chainArray[b] is {}".format(chainArray[b]) if b == 0: # at first joint, only equals root matrix chainMult = chainArray[b].inverse() else: chainMult.__imul__(chainArray[b].inverse()) print "chainMult at {} is {}".format(b, chainMult) print "endMat is {}".format(endMat) print "endMat x chainMult is {}".format( endMat.__mul__(chainMult)) #forwards # only need end in joint space endJSmat = endMat.__mul__(jntMat.inverse()) targetJSmat = targetRSmat * chainMult # check gap is still important gapVec = om.MVector(targetJSmat[12] - endJSmat[12], targetJSmat[13] - endJSmat[13], targetJSmat[14] - endJSmat[14]) gap = gapVec.length() if gap > tolerance: print "gap is {}".format(gap) print "end JS is {}".format(endJSmat) print "endMat v2 at {} is {}".format(i, endMat) print "target JS is {}".format(targetJSmat) targetJStrans = om.MTransformationMatrix( targetJSmat).translation(1) print "targetJStrans at {} is {}".format( i, targetJStrans) # we don't just want to aim each joint, we want to # aim the end, by rotating each joint in turn # first get the aim matrix from joint to end endAimMat = om.MMatrix() endAimMat = lookAt(jntMat, endJSmat) print "endAimMat is {}".format(endAimMat) #jntMat.__imul__(endAimMat) # then from that, aim from end to target targetAimMat = om.MMatrix() targetAimMat = lookAt(jntMat, targetJSmat) # is weighting this simple? #print "jntWeight is {}".format(jntWeights.__getitem__(i)) targetAimMat.__imul__(jntWeights.__getitem__(i)) print "targetAimMat is {}".format(targetAimMat) #constraints are going to be fun, but for now jntMat.__imul__(targetAimMat) endMat.__imul__(targetAimMat) print "finalEndMat is {}".format(endMat) #end of if block and aim procedure print "" else: print "" print "gap is within tolerance, ending" break jntArray.__setitem__(i, jntMat) #end of single iteration along joint chain print "" iter = iter + 1 #end of all iterations, computation has completed #convert jntArray of matrices to useful rotation values outArrayDH = pData.outputArrayValue(generalIk.aOutArray) targetRSTransA = om.MTransformationMatrix(targetRSmat).translation( 4) print "target in RS world is {}".format(targetRSTransA) for i in range(0, inLength): outArrayDH.jumpToPhysicalElement(i) outCompDH = outArrayDH.outputValue() outRotDH = outCompDH.child(generalIk.aOutRot) outRxDH = outRotDH.child(generalIk.aOutRx) outRyDH = outRotDH.child(generalIk.aOutRy) outRzDH = outRotDH.child(generalIk.aOutRz) outRotVals = om.MTransformationMatrix(jntArray[i]).rotation() # unitConversions bring SHAME on family xAngle = om.MAngle(outRotVals[0]) yAngle = om.MAngle(outRotVals[1]) zAngle = om.MAngle(outRotVals[2]) outRxDH.setMAngle(xAngle) outRyDH.setMAngle(yAngle) outRzDH.setMAngle(zAngle) outArrayDH.setAllClean() pData.setClean(pPlug)