Exemple #1
0
def addMirrorInfo(ctrlName):
    '''given a ctrlName add mirror info attributes to the ctrlName, so poses can be mirrored.
    This method needs to do a dot product on each axis and set the mirror info based on
    if the axes point towards or away. Then it sets the mirror info attr to be 1 or -1
    based on that test (per axis). This is used later to mirror poses.

    Channel box attributes are copied over, and depending on the -1 or 1 they are reversed
    or straight copied.

    Mirror Info is added by the base rig class at the very end of building.
    '''
    other = findMirrorCtrl(ctrlName)
    if not other:
        return
    mirrorInfo = [1, 1, 1]
    oXform = rigmath.Transform(other)
    cXform = rigmath.Transform(ctrlName)
    cXform.reflect()
    cx = cXform.xAxis()
    cy = cXform.yAxis()
    cz = cXform.zAxis()
    ox = oXform.xAxis()
    oy = oXform.yAxis()
    oz = oXform.zAxis()

    if cx.dot(ox) <= 0:
        mirrorInfo[0] = -1
    if cy.dot(oy) <= 0:
        mirrorInfo[1] = -1
    if cz.dot(oz) <= 0:
        mirrorInfo[2] = -1

    if not cmds.objExists(ctrlName + '.mirrorInfo'):
        cmds.addAttr(ctrlName, ln='mirrorInfo', at='double3', k=False)
        cmds.addAttr(ctrlName,
                     ln='mirrorInfoX',
                     at='double',
                     parent='mirrorInfo',
                     k=False)
        cmds.addAttr(ctrlName,
                     ln='mirrorInfoY',
                     at='double',
                     parent='mirrorInfo',
                     k=False)
        cmds.addAttr(ctrlName,
                     ln='mirrorInfoZ',
                     at='double',
                     parent='mirrorInfo',
                     k=False)

    cmds.setAttr(ctrlName + '.mirrorInfo', mirrorInfo[0], mirrorInfo[1],
                 mirrorInfo[2])
Exemple #2
0
def getDownAxis(joint):
    '''given a joint return the axis ('x','y', or 'z') that points towards child.
    On oriented joints this will be the same as the long axis, but if orientation is
    not know this function is slower but still produces an accurate result. 
    Joints at the end of the chain will use their parent instead to determine downAxis. 
    Negative axes aren't specified, function just returns x y or z.'''
    axes = 'xyz'
    target = cmds.listRelatives(joint, type='joint')
    if not target:
        target = cmds.listRelatives(joint, p=True, type='joint')
    if not target:
        raise RuntimeError(
            "no child or parent joints on %s, cannot determine downAxis" %
            joint)
    thisVector = rigmath.Vector(joint)
    thisXform = rigmath.Transform(joint)
    targVector = rigmath.Vector(target[0])
    diff = thisVector - targVector
    diff.normalize()

    #compare alignment of local axes of transform to diff vector
    dots = []
    for axis in (thisXform.xAxis(), thisXform.yAxis(), thisXform.zAxis()):
        dots.append(abs(diff.dot(axis)))
    return axes[dots.index(max(dots))]
Exemple #3
0
    def build(self):
        #make pyramids for world offsets
        ctrlXform = mpMath.Transform()
        ctrlXform.scale(2)
        zero, c1 = self.addCtrl('01',
                                type='FK',
                                shape='pyramid',
                                parent=self.limbNode,
                                shapeXform=ctrlXform)

        ctrlXform.scale(0.7)
        zero, c2 = self.addCtrl('02',
                                type='FK',
                                shape='pyramid',
                                parent=c1,
                                shapeXform=ctrlXform)

        ctrlXform.scale(0.7)
        zero, c3 = self.addCtrl('03',
                                type='FK',
                                shape='pyramid',
                                parent=c2,
                                shapeXform=ctrlXform)

        mpRig.addPickParent(c3, c2)
        mpRig.addPickParent(c2, c1)
Exemple #4
0
def changeCtrlShape(ctrl, newShape, shapeXform=None, size=1.0, segments=13):
    '''given a ctrl and a new shape, swap the shape. If shapeXform is given it will
    override the ctrl's shapeMatrix.
    '''
    newCrv = getCurve(shape=newShape, size=size, segments=segments)
    if not shapeXform:
        if cmds.objExists(ctrl + '.shapeMatrix'):
            shapeXform = rigmath.Transform(cmds.getAttr(ctrl + '.shapeMatrix'))
    if shapeXform:
        shapeXform.scale(size)
        cluster = cmds.cluster(newCrv)
        xfMatrix = rigmath.Transform(shapeXform)
        cmds.xform(cluster, ws=True, m=xfMatrix.get())
        cmds.delete(newCrv, ch=True)
        cmds.setAttr(ctrl + '.shapeMatrix', *shapeXform.get(), type='matrix')

    newShape = cmds.listRelatives(newCrv, s=True)[0]
    oldShape = cmds.listRelatives(ctrl, s=True)[0]
    cmds.connectAttr(newShape + ".local", oldShape + ".create", force=True)
    cmds.delete(cmds.cluster(oldShape))  #force update of crv shape
    cmds.delete(newCrv)
Exemple #5
0
def snapIKFK(ikctrl):
    '''Given an ik ctrl, snap the ik to the fk for that limb.  Uses messages on the ikctrl to find fk ctrls.
    pvPosMult: increase distance of poleVector control. Doesn't affect IK solution, just distance.
    '''
    # get all IK ctrls
    ikCtrlSet = getCtrlSetIK(ikctrl) or [ikctrl]
    allIKCtrls = cmds.sets(ikCtrlSet, q=True)

    #loop through twice, first doing end ctrls, then doing aims
    results = dict()
    for ctrlName in allIKCtrls:
        if not cmds.objExists(ctrlName + '.snapParents'):
            results[ctrlName] = None
            continue
        snapParents = cmds.listConnections(ctrlName + '.snapParents', s=1,
                                           d=0) or []
        #if there is one snap parent this is an end effector ctrl
        #do a simple snap
        if len(snapParents) == 1:
            results[ctrlName] = cmds.xform(snapParents[0],
                                           q=True,
                                           ws=True,
                                           m=True)

    for ctrlName in allIKCtrls:
        if not cmds.objExists(ctrlName + '.snapParents'):
            results[ctrlName] = None
            continue
        snapParents = cmds.listConnections(ctrlName + '.snapParents', s=1,
                                           d=0) or []
        #Use three parents to compute aim position
        if len(snapParents) == 3:
            aimV = getAimVector(snapParents[0], snapParents[1], snapParents[2])
            aimXform = rigmath.Transform(aimV)
            results[ctrlName] = aimXform.get()

    #bug fix for some IK limbs that need multiple snaps to reach the goal
    #This is because IK hierarchies can be arbitrary, and it's difficult to determine
    #which controls move others, so this takes an iterative approach
    for i in range(4):
        for ctrlName, value in results.iteritems():
            if not value:
                resetCtrl(ctrlName)
            else:
                cmds.xform(ctrlName, ws=True, m=value)

    # Turn on IK
    limbNode = getLimbNodeShape(allIKCtrls[0])
    cmds.setAttr(limbNode + '.' + name.FKIKBLENDATTR, 1)
Exemple #6
0
 def makeCtrl(self, startJoint, parent):
     '''recursive function to build ctrls'''
     ctrlXform = mpMath.Transform()
     ctrlXform.scale(0.3, 0.3, 0.3)
     zero, c1 = self.addCtrl('%02d' % len(self.ctrls),
                             type='FK',
                             shape='box',
                             parent=parent,
                             xform=startJoint,
                             shapeXform=ctrlXform)
     cmds.parentConstraint(c1, startJoint, mo=True)
     children = cmds.listRelatives(startJoint, type='joint') or []
     for child in children:
         childZero, childCtrl = self.makeCtrl(child, c1)
         mpRig.addPickParent(childCtrl, c1)
     return (zero, c1)
Exemple #7
0
def addCtrl(ctrlname,
            shape='sphere',
            size=1.0,
            segments=13,
            parent=None,
            color=None,
            shapeXform=None,
            xform=None):
    '''make a ctrl with a given shape out of a curve, parented under a zero null
    shape: sphere,cube,circle,cross,pyramid,line,spoon
    size: defaults to 1.0
    segments: used on round shapes, defaults to 13
    parent: object to parent under (if any)
    color: maya color name or color index
    shapeXform: a matrix or object (uses worldMatrix) to transform ctrl shape with. This is only cosmetic.
    xform: an object, vector, or matrix to xform the transform and rotationOrder of the ctrl
    '''
    #get shape with name
    crv = getCurve(shape, size=size, segments=segments)

    #rename and make zero null
    crv = cmds.rename(crv, ctrlname)
    zero = cmds.createNode('transform', n=crv + "_Zero")
    attr.hideAnimChannels(zero)

    #lock scale
    #In the rare cases that a ctrl must be scaled this can simply be unlocked.
    #Otherwise this saves a lot of headache later IMO.
    attr.lockAndHide(crv, 's')

    #parent under zero null so it's local xforms are zero
    cmds.parent(crv, zero)

    #set color based on argument, or position (based on name) if not specified
    if not color:
        color = getPositionColor(ctrlname)
    setColor(ctrlname, color)

    #do transforming (if given)
    if shapeXform:
        cluster = cmds.cluster(crv)
        xfMatrix = rigmath.Transform(shapeXform)
        cmds.xform(cluster, ws=True, m=xfMatrix.get())
        cmds.delete(crv, ch=True)
    else:
        shapeXform = rigmath.Transform()

    if parent:
        cmds.parent(zero, parent)

    #store shape transform for later, if needed
    cmds.addAttr(crv, dt='matrix', ln='shapeMatrix')
    shapeXform.scale(size)
    cmds.setAttr(crv + '.shapeMatrix', *shapeXform.get(), type='matrix')

    #flag
    setAsCtrl(crv)

    if xform:
        #if name of node then math matrix and rotate order
        if isinstance(xform, str) and cmds.objExists(xform):
            matchMatrix = cmds.xform(xform, q=True, ws=True, m=True)
            attr.matchAttr(xform, zero, "rotateOrder")
            attr.matchAttr(xform, crv, "rotateOrder")
        #otherwise just match matrix.
        #wrapping a Transform here for convenience
        else:
            try:
                matchMatrix = rigmath.Transform(xform).get()
            except RuntimeError:
                raise RuntimeError(
                    "Couldn't find object or transform %s to match" % xform)
        cmds.xform(zero, ws=True, m=matchMatrix)
    return (zero, crv)
Exemple #8
0
    def addFKIKChain(self, startJoint, endJoint, localParent, worldParent):
        '''Create a chain of FK ctrls with a blended IKRP solver. Requires three or more joints in chain.
        Also creates a "stub" joint with an SC solver at the end of the chain, so that the last joint's
        rotation is blended between the IK end ctrl and the last FK ctrl properly.
        - localParent = drives translation of FK chain always, rotation when local space is on. IK ignores.
        - worldParent = drives rotation when 'world' space is blended on. Drives IK translate and rotate.
        Returns list of [FkCtrl1,FKCtrl2,...,IKAim,IKEnd]
        '''
        jointList = mpJoint.getJointList(startJoint, endJoint)
        if len(jointList) < 3:
            raise RuntimeError('FKIKChain needs at least three joints')
        fkCtrls = self.addFKChain(startJoint, endJoint, localParent)
        aimCtrl, endCtrl = self.addIKChain(startJoint, endJoint, worldParent)

        #lock mid ctrls axes so the FK system can only rotate on one plane.
        #This is needed so the IK system, which is always on a plane, can snap to the FK system
        #First find which axis will be locked by checking its local axes against the
        #normal vector of the chain. The highest dot product is the most parallel.
        #Note: I may need to lock joint DoF as well, sometimes Maya injects tiny rotation values there
        #when in IK mode.
        midJointIdx = int(len(jointList) / 2)
        midJoint = jointList[midJointIdx]
        startV = mpMath.Vector(startJoint)
        midV = mpMath.Vector(midJoint)
        endV = mpMath.Vector(endJoint)
        chainMid = midV - startV
        chainEnd = endV - startV
        chainMid.normalize()
        chainEnd.normalize()
        chainNormal = chainMid.cross(chainEnd)
        chainNormal.normalize()
        axes = ['x', 'y', 'z']
        for ctrl in fkCtrls[1:-1]:
            ctrlXform = mpMath.Transform(ctrl)
            dots = list()
            dots.append(abs(ctrlXform.xAxis().dot(chainNormal)))
            dots.append(abs(ctrlXform.yAxis().dot(chainNormal)))
            dots.append(abs(ctrlXform.zAxis().dot(chainNormal)))
            del axes[dots.index(max(dots))]
            for axis in axes:
                mpAttr.lockAndHide(ctrl, ['r%s' % axis])

        #constrain fk ctrls to local/world
        firstFKZero = cmds.listRelatives(fkCtrls[0], p=True)[0]
        cmds.parentConstraint(localParent, firstFKZero, mo=True)

        #make a stub joint and an SCIKsolver
        #This is using Maya's built in 'ikBlend' blends rotate on the last joint
        self.name.desc = 'ikStub'
        cmds.select(cl=True)
        stubJoint = cmds.joint(n=self.name.get())
        cmds.parent(stubJoint, endJoint)
        stubPos = endV + ((endV - midV) * 0.5)
        cmds.xform(stubJoint, t=stubPos.get(), ws=True)
        self.name.desc = 'iKStubHandle'
        stubHandle, stubEffector = cmds.ikHandle(n=self.name.get(),
                                                 solver='ikSCsolver',
                                                 sj=endJoint,
                                                 ee=stubJoint)
        self.name.desc = 'stubEffector'
        stubEffector = cmds.rename(stubEffector, self.name.get())
        cmds.parent(stubHandle, self.noXform)
        cmds.parentConstraint(endCtrl, stubHandle, mo=True)
        mpCache.flag(stubJoint,
                     False)  #don't want stub joints saved in jointSRTs

        #Construct the blend
        FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR,
                                       at='float',
                                       min=0,
                                       max=1,
                                       dv=0,
                                       k=True)
        effector, handle = mpJoint.getIKNodes(endJoint)
        cmds.connectAttr(FKIKblender, handle + '.ikBlend')
        cmds.connectAttr(FKIKblender, stubHandle + '.ikBlend')

        #switch ctrl vis
        for ctrl in (aimCtrl, endCtrl):
            shape = cmds.listRelatives(ctrl, s=True)[0]
            mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999)
        for ctrl in fkCtrls:
            shape = cmds.listRelatives(ctrl, s=True)[0]
            adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v',
                                          -0.4999999)
            mpAttr.connectWithReverse(adder + '.output',
                                      shape + '.v',
                                      force=True)

        #setup IK->FK snapping messages
        #Since the IK end ctrl and the last FK ctrl can have totally different oris,
        #make a null matching the IK's ori under the FK ctrl to act as a snap target
        self.name.desc = 'ikEndSnap'
        endSnapNull = cmds.group(em=True, n=self.name.get(), p=fkCtrls[-1])
        cmds.xform(endSnapNull,
                   ws=True,
                   m=cmds.xform(endCtrl, q=True, ws=True, m=True))
        mpRig.addSnapParent(endCtrl, endSnapNull)
        mpRig.addSnapParent(aimCtrl, fkCtrls[0])
        mpRig.addSnapParent(aimCtrl, fkCtrls[1])
        mpRig.addSnapParent(aimCtrl, fkCtrls[2])

        #cleanup
        for hideObject in (handle, stubHandle, stubJoint):
            cmds.setAttr(hideObject + '.v', 0)
            mpAttr.lockAndHide(hideObject, 'v')

        fkCtrls.extend([aimCtrl, endCtrl])
        return fkCtrls
Exemple #9
0
    def build(self):
        self.addPinParent()
        self.addAttrLimb(ln='noStretch',
                         at='float',
                         min=0,
                         max=1,
                         dv=0,
                         k=True,
                         s=1)
        self.addAttrLimb(ln='slideAlong',
                         at='float',
                         min=-1,
                         max=1,
                         dv=0,
                         k=True,
                         s=1)
        jointList = mpJoint.getJointList(self.startJoint, self.endJoint)
        if len(jointList) < 2:
            raise RuntimeError(
                'NurbsStrip requires at least 2 joints in chain. Got %s' %
                len(jointList))

        #Create NURBS strip by making curves along joints, and a cross section crv, then extruding
        crv = mpNurbs.curveFromNodes(jointList)
        crossCurve = cmds.curve(d=1,
                                p=[(0, 0, -0.5 * self.stripWidth),
                                   (0, 0, 0.5 * self.stripWidth)],
                                k=(0, 1))
        cmds.select([crossCurve, crv], r=1)
        surf = cmds.extrude(ch=False,
                            po=0,
                            et=2,
                            ucp=1,
                            fpt=1,
                            upn=1,
                            rotation=0,
                            scale=1,
                            rsp=1)[0]
        cmds.delete([crv, crossCurve])
        self.name.desc = 'driverSurf'
        surf = cmds.rename(surf, self.name.get())
        cmds.parent(surf, self.noXform)

        #Rebuild strip to proper number of spans
        cmds.rebuildSurface(surf,
                            ch=0,
                            rpo=1,
                            rt=0,
                            end=1,
                            kr=0,
                            kcp=0,
                            kc=1,
                            sv=self.numSpans,
                            su=0,
                            du=1,
                            tol=0.01,
                            fr=0,
                            dir=2)

        #make live curve on surface down the middle
        #this is used later for noStretch
        curvMaker = cmds.createNode('curveFromSurfaceIso', n=surf + "CurveIso")
        cmds.setAttr(curvMaker + ".isoparmValue", 0.5)
        cmds.setAttr(curvMaker + ".isoparmDirection", 1)
        cmds.connectAttr(surf + ".worldSpace[0]", curvMaker + ".inputSurface")
        offsetCrvShp = cmds.createNode("nurbsCurve",
                                       n=crv + "_driverSurfCrvShape")
        offsetCrv = cmds.listRelatives(p=1)[0]
        offsetCrv = cmds.rename(offsetCrv, crv + "_driverSurfCrv")
        cmds.connectAttr(curvMaker + ".outputCurve", offsetCrvShp + ".create")
        cmds.parent(offsetCrv, self.noXform)

        #Measure curve length and divide by start length
        #to get a normalized value that is useful later to control stretch
        crvInfo = cmds.createNode('curveInfo', n=offsetCrv + "Info")
        cmds.connectAttr(offsetCrv + ".worldSpace[0]", crvInfo + ".ic")
        arcLength = cmds.getAttr(crvInfo + ".al")
        stretchAmountNode = cmds.createNode('multiplyDivide',
                                            n=offsetCrv + "Stretch")
        cmds.setAttr(stretchAmountNode + ".op", 2)  #divide
        cmds.setAttr(stretchAmountNode + ".input1X", arcLength)
        cmds.connectAttr(crvInfo + ".al", stretchAmountNode + ".input2X")

        #Stretch Blender blends start length with current length
        #and pipes it back into stretchAmoundNode's startLength, so user can turn
        #stretch behavior on or off
        stretchBlender = cmds.createNode('blendColors',
                                         n=offsetCrv + "StretchBlender")
        cmds.setAttr(stretchBlender + ".c1r", arcLength)
        cmds.connectAttr(crvInfo + ".al", stretchBlender + ".c2r")
        cmds.connectAttr(stretchBlender + ".opr",
                         stretchAmountNode + ".input1X")
        cmds.connectAttr(self.limbNode + ".noStretch",
                         stretchBlender + ".blender")

        #attach joints to surface
        closestPoint = cmds.createNode('closestPointOnSurface', n='tempClose')
        cmds.connectAttr(surf + ".worldSpace[0]",
                         closestPoint + ".inputSurface")
        for idx, jnt in enumerate(jointList):
            self.name.desc = 'jnt%02dOffset' % idx
            locator = cmds.spaceLocator(n=self.name.get())[0]
            cmds.setAttr(locator + '.localScale', self.stripWidth,
                         self.stripWidth, self.stripWidth)
            cmds.parent(locator, self.noXform)
            #Use closest point to to find jnt's percent along the curve
            cmds.setAttr(closestPoint + '.ip',
                         *cmds.xform(jnt, q=True, t=True, ws=True))
            percentage = cmds.getAttr(closestPoint + '.r.v')
            #attach to surface
            posNode, aimCnss, moPath, slider = self.attachObjToSurf(
                locator, surf, offsetCrv, stretchAmountNode, percentage)

            cmds.connectAttr(self.limbNode + ".slideAlong", slider + ".i2")
            cmds.parentConstraint(locator, jnt, mo=True)
        cmds.delete(closestPoint)

        #add controls.These drive new joints which skinCluster the NURBS strips
        stripJoints = []
        stripJointParent = cmds.createNode('transform',
                                           n=crv + "_stripJoints",
                                           p=self.noXform)
        ctrlParent = cmds.createNode('transform',
                                     n=crv + "_Ctrls",
                                     p=self.pinParent)
        cmds.xform(ctrlParent,
                   ws=True,
                   m=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])

        prevCtrl = None
        for i in range(self.numCtrls):
            ctrlXform = mpMath.Transform(jointList[0])
            zero, ctrl = self.addCtrl('Ctrl%02d' % i,
                                      type='FK',
                                      shape='box',
                                      parent=ctrlParent,
                                      xform=ctrlXform)
            ctrlXform.scale(0.8, 0.8, 0.8)
            tZero, tCtrl = self.addCtrl('TweakCtrl%02d' % i,
                                        type='FK',
                                        shape='cross',
                                        parent=ctrl,
                                        xform=ctrlXform)

            #Make the new joint for the control to drive
            cmds.select(clear=True)
            self.name.desc = 'StripJnt%02d' % i
            jnt = cmds.joint(p=(0, 0, 0), n=self.name.get())
            cmds.parentConstraint(tCtrl, jnt, mo=False)

            #briefly attach ctrls to strip to align them
            percentage = float(i) / (self.numCtrls - 1.0)
            if i > 0 and i < self.numCtrls - 1:
                percentage = self.uMin + (percentage * (self.uMax - self.uMin))
            cmds.delete(
                self.attachObjToSurf(zero, surf, offsetCrv, stretchAmountNode,
                                     percentage))
            cmds.parent(jnt, stripJointParent)
            stripJoints.append(jnt)

            if prevCtrl:
                cmds.parent(zero, prevCtrl)
                mpRig.addPickParent(ctrl, prevCtrl)
            prevCtrl = ctrl

        #skin strip to controls
        #Can get some different behavior by chaning the strip's weights
        #or perhaps using dual quat. mode on the skinCluster
        skinObjs = stripJoints + [surf]
        cmds.skinCluster(
            skinObjs,
            bindMethod=0,  #closest Point
            sm=0,  #standard bind method
            ih=True,  #ignore hierarchy
        )
Exemple #10
0
def orientToRot(jnt):
    '''copy orient to rotation'''
    origTransform = rigmath.Transform(jnt)
    cmds.setAttr(jnt + '.jointOrient', 0, 0, 0)
    cmds.xform(jnt, ws=True, m=origTransform.get())
Exemple #11
0
    def build(self):
        jointList = mpJoint.getJointList(self.startJoint, self.endJoint)
        if len(jointList) < 5:
            raise RuntimeError(
                'LegFKIK requires at least 5 joints in chain: hip/knee/ankle/ball/toetip. Got %s'
                % len(jointList))

        #start with standard blend setup and FKIKChain
        self.addPinBlend()
        legCtrls = self.addFKIKChain(jointList[0], jointList[-3],
                                     self.pinBlend, self.pinWorld)

        #add FK offsets to ball
        ballFKZero, ballFKCtrl = self.addCtrl('%02d' % len(jointList[:-2]),
                                              type='FK',
                                              shape='sphere',
                                              parent=legCtrls[-3],
                                              xform=jointList[-2])
        mpAttr.lockAndHide(ballFKCtrl, 't')
        cmds.orientConstraint(ballFKCtrl, jointList[-2], mo=True)

        #addFKIKChain returns list with [...,lastFK,IKAim,IKEnd], so grabbing last FK for pick parent:
        mpRig.addPickParent(ballFKCtrl, legCtrls[-3])

        #create classic 'reverse foot' setup
        if self.heel:
            heelPos = mpMath.Transform(self.heel)
        else:
            RIGLOG.warning(
                'No ".heel" joint specified on LegFKIK, guessing pivot')
            #take the ball->toe length, double it, and go back from the ball that much
            heelOffset = mpMath.Vector(jointList[-2])  #ball
            heelOffset -= mpMath.Vector(jointList[-1])  #minus toe
            heelOffset *= 2
            heelOffset + mpMath.Vector(jointList[-2])  #add back to ball
            heelPos = mpMath.Transform(
                jointList[-2])  #heel equal ball plus our new vector
            heelPos += heelOffset

        #Make ik single chains for ball and toe
        self.name.desc = 'ikHandleBall'
        ballHandle, ballEffector = cmds.ikHandle(n=self.name.get(),
                                                 solver='ikSCsolver',
                                                 sj=jointList[-3],
                                                 ee=jointList[-2])
        self.name.desc = 'ikHandleToe'
        toeHandle, toeEffector = cmds.ikHandle(n=self.name.get(),
                                               solver='ikSCsolver',
                                               sj=jointList[-2],
                                               ee=jointList[-1])

        self.name.desc = 'ballEffector'
        ballEffector = cmds.rename(ballEffector, self.name.get())
        cmds.parent(ballHandle, self.noXform)
        self.name.desc = 'toeEffector'
        toeEffector = cmds.rename(toeEffector, self.name.get())
        cmds.parent(toeHandle, self.noXform)

        #Make foot controls
        #These transforms are for cosmetic SRT of the foot controls
        anklePos = mpMath.Vector(jointList[-3])
        heelVec = mpMath.Vector(heelPos)
        footCtrlXform = mpMath.Transform()
        footCtrlXform.scale(2, 0.4, 4)
        footCtrlXform.translate(0, heelVec.y - anklePos.y, 0)
        toeCtrlXform = mpMath.Transform()
        toeCtrlXform.setFromEuler(0, 90, 0)

        footZero, footCtrl = self.addCtrl('Foot',
                                          shape='cube',
                                          type='IK',
                                          parent=self.pinWorld,
                                          xform=mpMath.Vector(jointList[-3]),
                                          shapeXform=footCtrlXform)
        heelZero, heelCtrl = self.addCtrl('Heel',
                                          shape='circle',
                                          type='IK',
                                          parent=footCtrl,
                                          xform=heelPos,
                                          shapeXform=toeCtrlXform)
        toeTipZero, toeTipCtrl = self.addCtrl('ToeTip',
                                              shape='circle',
                                              type='IK',
                                              parent=heelCtrl,
                                              xform=jointList[-1],
                                              shapeXform=toeCtrlXform)
        ballZero, ballCtrl = self.addCtrl('Ball',
                                          shape='circle',
                                          type='IK',
                                          parent=toeTipCtrl,
                                          xform=jointList[-2],
                                          shapeXform=toeCtrlXform)

        cmds.parentConstraint(ballCtrl, ballHandle, mo=True)
        cmds.parentConstraint(toeTipCtrl, toeHandle, mo=True)

        #Blend already exists, but this will grab the right attr
        FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR,
                                       at='float',
                                       min=0,
                                       max=1,
                                       dv=0,
                                       k=True)
        cmds.connectAttr(FKIKblender, ballHandle + '.ikBlend')
        cmds.connectAttr(FKIKblender, toeHandle + '.ikBlend')

        #constrain legIK endctrl to new foot ctrl
        ankleIKCtrl = legCtrls[-1]
        mpAttr.unlockAndShow(ankleIKCtrl, 'r')
        cmds.parentConstraint(ballCtrl, ankleIKCtrl, mo=True)

        #swap out foot for old IK handle ctrl
        #First retrieve effector, handle, aim, and endNull from IK system
        effector, handle = mpJoint.getIKNodes(jointList[-3])
        handleCns = cmds.listConnections(handle + '.tx', s=1, d=0)[0]
        endNull = cmds.listConnections(handleCns +
                                       '.target[0].targetTranslate',
                                       s=1,
                                       d=0)[0]
        endNullCns = cmds.listConnections(endNull + '.tx', s=1, d=0)[0]
        aimCtrl = legCtrls[-2]
        aimCtrlZero = cmds.listRelatives(aimCtrl, p=True)[0]

        #delete old aimCtrl blend cns
        cmds.delete(cmds.listConnections(aimCtrlZero + '.tx', s=1, d=0)[0])
        cmds.pointConstraint(self.pinWorld, footCtrl, aimCtrlZero, mo=True)

        #delete old IK ctrl and wire IK to new foot ctrl
        cmds.delete(endNullCns)
        cmds.parentConstraint(ballCtrl, endNull, mo=True)
        self.deleteCtrl(legCtrls[-1])

        #add new pickwalk/snap info
        mpRig.addPickParent(footCtrl, legCtrls[-2])
        mpRig.addPickParent(ballCtrl, footCtrl)
        mpRig.addPickParent(toeTipCtrl, ballCtrl)
        mpRig.addPickParent(heelCtrl, toeTipCtrl)

        #Make some nulls to act as snap targets. This is because IK and FK controls might have different axis order or initial positions.
        self.name.desc = 'ikAnkleSnapTarget'
        ikAnkleSnapTarget = cmds.group(em=True,
                                       n=self.name.get(),
                                       p=legCtrls[-3])
        cmds.xform(ikAnkleSnapTarget,
                   ws=True,
                   m=cmds.xform(footCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(footCtrl, ikAnkleSnapTarget)

        self.name.desc = 'ikBallSnapTarget'
        ikBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl)
        cmds.xform(ikBallSnapTarget,
                   ws=True,
                   m=cmds.xform(ballCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(ballCtrl, ikBallSnapTarget)

        self.name.desc = 'fkBallSnapTarget'
        fkBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballCtrl)
        cmds.xform(fkBallSnapTarget,
                   ws=True,
                   m=cmds.xform(ballFKCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(ballFKCtrl, fkBallSnapTarget)

        self.name.desc = 'ikToeSnapTarget'
        ikToeSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl)
        cmds.xform(ikToeSnapTarget,
                   ws=True,
                   m=cmds.xform(toeTipCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(toeTipCtrl, ikToeSnapTarget)

        #add new ctrls to vis switch
        for ctrl in (footCtrl, heelCtrl, toeTipCtrl, ballCtrl):
            shape = cmds.listRelatives(ctrl, s=True)[0]
            mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999)
        for ctrl in [ballFKCtrl]:
            shape = cmds.listRelatives(ctrl, s=True)[0]
            adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v',
                                          -0.4999999)
            revNode = mpAttr.connectWithReverse(adder + '.output',
                                                shape + '.v',
                                                force=True)

        #cleanup
        for item in [ballHandle, toeHandle, handle]:
            mpAttr.visOveride(item, 0)
Exemple #12
0
    def build(self):
        jointList = mpJoint.getJointList(self.startJoint, self.endJoint)
        if len(jointList) < 5:
            raise RuntimeError(
                'LegFKIK requires at least 5 joints in chain: hip/knee/ankle/ball/toetip. Got %s'
                % len(jointList))

        #Internally the dog leg is driven by a 3 joint ik system made here.
        #This three joint IK has the same total length as hip->ball joint chain.
        length = mpJoint.getChainLength(jointList[0], jointList[-2])
        halfLength = length / 2.0

        hip = mpMath.Vector(jointList[0])
        knee = mpMath.Vector(jointList[1])
        ball = mpMath.Vector(jointList[-2])

        legBaseV = ball - hip
        legBaseMidV = legBaseV * 0.5
        binormalLength = -1 * math.sqrt(
            0.5 * legBaseV.length()**2 / halfLength**2)

        #Find the isoceles triange that is coplanar with the leg chain,
        #and whose tall legs sum to the same length as the dogleg.
        #The top vertex of this triangle is where we want the mid joint on our
        #three joint system
        kneeV = knee - hip
        kneeV.normalize()
        ballV = ball - hip
        ballV.normalize()
        chainNormal = kneeV.cross(ballV)
        chainNormal.normalize()
        chainBinorm = chainNormal.cross(kneeV)
        chainBinorm *= binormalLength
        midJointPos = (legBaseMidV + chainBinorm) + hip

        #make driver joints
        cmds.select(cl=True)
        self.name.desc = 'driverJoint01'
        driverJnt1 = cmds.joint(n=self.name.get(), p=hip.get())
        self.name.desc = 'driverJoint02'
        driverJnt2 = cmds.joint(n=self.name.get(), p=midJointPos.get())
        self.name.desc = 'driverJoint03'
        driverJnt3 = cmds.joint(n=self.name.get(), p=ball.get())

        #start with standard blend setup and FKIKChain on hip->ankle
        self.addPinBlend()
        legCtrls = self.addFKIKChain(jointList[0], jointList[-3],
                                     self.pinBlend, self.pinWorld)
        ikEnd = legCtrls[-1]
        effector, handle = mpJoint.getIKNodes(jointList[-3])

        #add FK offsets to ball
        ballFKZero, ballFKCtrl = self.addCtrl('%02d' % len(jointList[:-2]),
                                              type='FK',
                                              shape='sphere',
                                              parent=legCtrls[-3],
                                              xform=jointList[-2])
        mpAttr.lockAndHide(ballFKCtrl, 't')
        cmds.orientConstraint(ballFKCtrl, jointList[-2], mo=True)
        mpRig.addPickParent(ballFKCtrl, legCtrls[-3])

        #Make IK Chain on driver joints
        oldpart = self.name.part
        self.name.part += "Driver"
        driverAimCtrl, driverEndCtrl = self.addIKChain(driverJnt1, driverJnt3,
                                                       self.pinWorld)
        self.name.part = oldpart

        #set ctrl flags false on driver ctrls.
        #This way animators will never touch them and they won't snap/reset/etc.
        driverEndNull = cmds.listRelatives(driverEndCtrl, p=True)[0]
        mpCtrl.setAsCtrl(driverEndCtrl, False)
        mpCtrl.setAsCtrl(driverAimCtrl, False)
        #and delete shapes so they aren't visible
        for ctrl in (driverEndCtrl, driverAimCtrl):
            self.ctrls.remove(ctrl)
            cmds.delete(cmds.listRelatives(ctrl, s=True)[0])

        #make single chain on ankle->ball and ball->tip for ankle roll and toe tap
        self.name.desc = 'ikHandleBall'
        ballHandle, ballEffector = cmds.ikHandle(n=self.name.get(),
                                                 solver='ikSCsolver',
                                                 sj=jointList[-3],
                                                 ee=jointList[-2])
        self.name.desc = 'ikHandleToe'
        toeHandle, toeEffector = cmds.ikHandle(n=self.name.get(),
                                               solver='ikSCsolver',
                                               sj=jointList[-2],
                                               ee=jointList[-1])

        self.name.desc = 'ballEffector'
        ballEffector = cmds.rename(ballEffector, self.name.get())
        cmds.parent(ballHandle, self.noXform)
        self.name.desc = 'toeEffector'
        toeEffector = cmds.rename(toeEffector, self.name.get())
        cmds.parent(toeHandle, self.noXform)

        #Make a ankle roll ctrl and toe tap ctrl
        #These transforms are for cosmetic SRT of the foot controls
        footCtrlXform = mpMath.Transform()
        footCtrlXform.scale(2, 0.4, 4)
        toeCtrlXform = mpMath.Transform()
        toeCtrlXform.setFromEuler(0, 90, 0)

        footZero, footCtrl = self.addCtrl('Foot',
                                          shape='cube',
                                          type='IK',
                                          parent=self.pinWorld,
                                          xform=mpMath.Vector(jointList[-2]),
                                          shapeXform=footCtrlXform)
        toeTipZero, toeTipCtrl = self.addCtrl('ToeTip',
                                              shape='circle',
                                              type='IK',
                                              parent=footCtrl,
                                              xform=jointList[-1],
                                              shapeXform=toeCtrlXform)
        ballZero, ballCtrl = self.addCtrl('Ball',
                                          shape='circle',
                                          type='IK',
                                          parent=toeTipCtrl,
                                          xform=jointList[-2],
                                          shapeXform=toeCtrlXform)

        #parent constrain single chains under new ctrls
        cmds.parentConstraint(ballCtrl, ballHandle, mo=True)
        cmds.parentConstraint(toeTipCtrl, toeHandle, mo=True)
        cmds.parentConstraint(footCtrl, driverEndNull, mo=True)

        #parent constrain ankle IK to new foot ctrls
        driverEffector, driverHandle = mpJoint.getIKNodes(driverJnt3)
        mpAttr.unlockAndShow(ikEnd, 'r')
        ikEndNull = cmds.listRelatives(ikEnd, p=True)[0]
        cmds.parentConstraint(ballCtrl, ikEndNull, mo=True)
        cmds.parentConstraint(toeTipCtrl, driverHandle, mo=True)

        #parent constrain ball ctrl to driver knee joint
        cmds.parentConstraint(driverJnt2, ballZero, mo=True)

        #Blend already exists, but this will grab the right attr
        FKIKblender = self.addAttrLimb(ln=mpName.FKIKBLENDATTR,
                                       at='float',
                                       min=0,
                                       max=1,
                                       dv=0,
                                       k=True)
        cmds.connectAttr(FKIKblender, ballHandle + '.ikBlend')
        cmds.connectAttr(FKIKblender, toeHandle + '.ikBlend')

        #add new pickwalk/snap info
        mpRig.addPickParent(footCtrl, legCtrls[-2])
        mpRig.addPickParent(ballCtrl, footCtrl)
        mpRig.addPickParent(toeTipCtrl, ballCtrl)

        #Make some nulls to act as snap targets. This is because IK and FK controls might have different axis order or initial positions.
        self.name.desc = 'ikAnkleSnapTarget'
        ikAnkleSnapTarget = cmds.group(em=True,
                                       n=self.name.get(),
                                       p=jointList[-2])
        cmds.xform(ikAnkleSnapTarget,
                   ws=True,
                   m=cmds.xform(footCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(footCtrl, ikAnkleSnapTarget)

        self.name.desc = 'ikBallSnapTarget'
        ikBallSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl)
        cmds.xform(ikBallSnapTarget,
                   ws=True,
                   m=cmds.xform(ballCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(ballCtrl, ikBallSnapTarget)

        self.name.desc = 'fkBallSnapTarget'
        fkBallSnapTarget = cmds.group(em=True,
                                      n=self.name.get(),
                                      p=jointList[-2])
        cmds.xform(fkBallSnapTarget,
                   ws=True,
                   m=cmds.xform(ballFKCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(ballFKCtrl, fkBallSnapTarget)

        self.name.desc = 'ikToeSnapTarget'
        ikToeSnapTarget = cmds.group(em=True, n=self.name.get(), p=ballFKCtrl)
        cmds.xform(ikToeSnapTarget,
                   ws=True,
                   m=cmds.xform(toeTipCtrl, ws=True, q=True, m=True))
        mpRig.addSnapParent(toeTipCtrl, ikToeSnapTarget)

        #add new ctrls to vis switch
        for ctrl in (footCtrl, toeTipCtrl, ballCtrl):
            shape = cmds.listRelatives(ctrl, s=True)[0]
            mpAttr.connectWithAdd(FKIKblender, shape + '.v', 0.4999999)
        for ctrl in [ballFKCtrl]:
            shape = cmds.listRelatives(ctrl, s=True)[0]
            adder = mpAttr.connectWithAdd(FKIKblender, shape + '.v',
                                          -0.4999999)
            mpAttr.connectWithReverse(adder + '.output',
                                      shape + '.v',
                                      force=True)

        #cleanup
        for item in [ballHandle, toeHandle, handle]:
            mpAttr.visOveride(item, 0)
        mpAttr.visOveride(driverJnt1, 0)