Exemple #1
0
def setPose(name, node=None, ns=""):
    """ Reads the attributes off all objects in the specified set (name) and sets those as the set values
    :param name str: name of the pose to set
    :param node str: if specified, only applies the pose on the specified node
    """
    if name == 'default': raise NameError("Can't override the default pose!")
    setName = ns + names.nodeName(
        'set', names.getDescriptor(name), side=names.getSide(name))
    if node:
        node = node.replace(names.getNS(node), "")
        nodes = [ns + node]
    else:
        nodes = util.objectSet.getSetContents(setName)

    for node in nodes:
        if not cmds.objExists("{0}.{1}".format(node, name)):
            cmds.error("{0} is not part of the pose {1}".format(node, name))
        data = json.loads(cmds.getAttr("{0}.{1}".format(node, name)))

        for attr in data:
            val = cmds.getAttr("{0}.{1}".format(node, attr))
            data[attr] = val

        prettyData = json.dumps(data,
                                sort_keys=True,
                                indent=3,
                                separators=(',', ': '))

        cmds.setAttr("{0}.{1}".format(node, name), prettyData, type="string")
Exemple #2
0
def loadPoses(nameList, path, ns=""):
    """ Tries to load all poses in specified location with specfied names
    :param nameList list : list of pose names to look for
    :param path str: folder to look in
    """
    if not os.path.exists(path):
        raise NameError("The path '{0}' does not exist!".format(path))
    if not cmds.objExists(ns + basePoseSet):
        raise ValueError("This scene does not have a base pose set!")

    for name in nameList:
        setName = ns + names.nodeName(
            'set', names.getDescriptor(name), side=names.getSide(name))
        if not cmds.objExists(setName):
            cmds.sets(setName, add=ns + basePoseSet)
        dataPath = os.path.join(path, name + "Pose.json")

        data = json.loads(open(dataPath).read())

        for node in data:
            if not cmds.objExists("{0}.{1}".format(ns + node, name)): continue
            newData = {}
            for attr in data[node]:
                val = data[node][attr]
                newData[attr] = val

            prettyData = json.dumps(newData,
                                    sort_keys=True,
                                    indent=3,
                                    separators=(',', ': '))

            cmds.setAttr("{0}.{1}".format(ns + node, name),
                         prettyData,
                         type="string")
Exemple #3
0
def savePoses(nameList, path, ns=""):
    """ Saves out all poses in nameList to specified path. Creates a new file per pose
    :param nameList list: list of pose names to save out
    :param path str: path to save to, should be in the style "/test/path" and cannot be a file
    """
    if not os.path.exists(path):
        raise NameError("The path '{0}' does not exist!".format(path))
    for name in nameList:
        setName = names.nodeName('set',
                                 names.getDescriptor(name),
                                 side=names.getSide(name))
        nodes = util.objectSet.getSetContents(setName)
        data = {}
        for node in nodes:
            data[node] = {}
            if not cmds.objExists("{0}.{1}".format(node, name)):
                cmds.error("{0} is not part of the pose {1}".format(
                    node, name))
            poseData = json.loads(cmds.getAttr("{0}.{1}".format(node, name)))

            for attr in poseData:
                val = cmds.getAttr("{0}.{1}".format(node, attr))
                poseData[attr] = val

            data[node] = poseData

        prettyData = json.dumps(data,
                                sort_keys=True,
                                indent=3,
                                separators=(',', ': '))

        # Create a file name
        dataPath = os.path.join(path, name + "Pose.json")
        if os.path.exists(dataPath):
            # os.rename and shutil.move both throws errors, doing it the manual way...
            old = json.loads(open(dataPath).read())
            f = open(dataPath + "OLD", 'w')
            prettyData = json.dumps(old,
                                    sort_keys=True,
                                    indent=3,
                                    separators=(',', ': '))
            print >> f, prettyData
            f.close()
            os.remove(dataPath)

        # Save the data to disc. Running json.dumps and printing line by line returns an easily readable json
        f = open(dataPath, 'w')
        prettyData = json.dumps(data,
                                sort_keys=True,
                                indent=3,
                                separators=(',', ': '))
        print >> f, prettyData
        f.close()
Exemple #4
0
def createPoses(node, nameList=[], extra=[], ns=""):
    """ Creates poses specified in the nameList on specified node.
    :param node str: node to add poses to
    :param nameList list: list of pose names
    :param extra list: appends the attributes specified in this list to the default list of attrs
    
    Example:
        from ooutdmaya.rigging.core.util import pose
        reload(pose)
        
        if not cmds.objExists('pose_set'): cmds.sets('pose_set')
        
        mySphere = cmds.polySphere(n="test1_geo", ch=0)[0]
        cmds.addAttr(mySphere, ln="testAttr", at="float", k=True, dv=10)
        pose.createPoses(mySphere, ['default', 'test'], ['testAttr])
    """
    if not node: return
    if not cmds.objExists(ns + node): return

    attrList = [
        'tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz', 'v',
        'rotateOrder'
    ]

    if extra: attrList = attrList + extra

    data = {}

    for attr in attrList:
        val = cmds.getAttr("{0}.{1}".format(ns + node, attr))
        data[attr] = val

    prettyData = json.dumps(data,
                            sort_keys=True,
                            indent=3,
                            separators=(',', ': '))

    for name in nameList:
        cmds.addAttr(ns + node, ln=name, dt="string", h=False)
        cmds.setAttr("{0}.{1}".format(ns + node, name),
                     prettyData,
                     type="string")

        setName = ns + names.nodeName(
            'set', names.getDescriptor(name), side=names.getSide(name))

        if cmds.objExists(setName):
            cmds.sets(ns + node, add=setName)
        else:
            newSet = util.lib.createNode('objectSet', name=ns + setName)
            cmds.sets(newSet, add=ns + basePoseSet)
            cmds.sets(ns + node, add=newSet)
Exemple #5
0
def applyPose(name, node=None, ns=""):
    """ Applies the pose specified (name) to all objects in the relevant pose set
    :param name str: name of the pose to apply
    """
    setName = ns + names.nodeName(
        'set', names.getDescriptor(ns + name), side=names.getSide(ns + name))
    print setName
    if node:
        node = node.replace(names.getNS(node), "")
        nodes = [ns + node]
    else:
        nodes = util.objectSet.getSetContents(setName)

    for node in nodes:
        if not cmds.objExists("{0}.{1}".format(node, name)):
            cmds.error("{0} is not part of the pose {1}".format(node, name))
        data = json.loads(cmds.getAttr("{0}.{1}".format(node, name)))

        for attr in data:
            try:
                cmds.setAttr("{0}.{1}".format(node, attr), data[attr])
            except:
                continue
Exemple #6
0
def getDescriptor(*args, **kwargs):
    # cmds.warning('ooutdmaya.rigging.core.util.names.getDescriptor now DEPRECIATED... please use\n\tooutdmaya.common.util.names.getDescriptor instead')
    return names.getDescriptor(*args, **kwargs)
Exemple #7
0
 def run(self):
     """
     """
     
     # Run inherited functionality
     base.Base.run(self)
     
     # Create groups      
     self.driverChain = self.chain
     #=======================================================================
     # self.driverChain = joint.duplicateSegment(self.chain, suffix='DRV',
     #                                       searchReplaceSetList=[(names.getNS(self.chain[0]), '')])
     #=======================================================================
     
     self.jawOffset = cmds.createNode('transform', n=names.nodeName('transform', '{0}{1}Offset'.format(self.prefix, self.name), self.side))
     chainParent = cmds.listRelatives(self.chain[0], p=1)[0]
     if chainParent:
         easy.snapAlignScaleTo(chainParent, self.jawOffset)
     
     cmds.parent(self.jawOffset, self.grpSkeleton)
     cmds.parent(self.driverChain[0], self.jawOffset)
     
     key = 'jaw'
     self.buildCtl(key, lockAttrList=['scale'], movablePivot=False, keyAttrList=['rotateOrder'])
     ctlDict = self.ctls[key]
     easy.snapAlignTo(self.chain[-1], ctlDict['input'])
     
     self.sdkZeroGrp = cmds.createNode('transform', n=names.nodeName('transform', '{0}{1}SDKZero'.format(self.prefix, self.name), self.side))
     if chainParent:
         cmds.parent(self.sdkZeroGrp, chainParent)
         cmds.parentConstraint(chainParent, self.ctls['jaw']['input'], mo=1,
                               n=names.addModifier(self.ctls['jaw']['input'], 'parentConstraint'))
     
     cmds.delete(cmds.parentConstraint(self.chain[0], self.sdkZeroGrp))
     cmds.delete(cmds.scaleConstraint(self.chain[0], self.sdkZeroGrp))
     
     cmds.delete(cmds.aimConstraint(self.chain[-1], self.sdkZeroGrp, aimVector=[1, 0, 0],
         upVector=[0, 0, 1], worldUpType='objectrotation', worldUpVector=self.jawBaseUpVector,
         worldUpObject=chainParent))
     
     # Create a rotation set driven key transform
     self.sdkRotGrp = cmds.duplicate(self.sdkZeroGrp, n=names.addModifier(self.sdkZeroGrp, 'transform', addSuffix="SetDrivenRot"))[0]
     
     # Create a position set driven key transform
     self.sdkPosGrp = cmds.duplicate(self.sdkZeroGrp, n=names.addModifier(self.sdkZeroGrp, 'transform', addSuffix="SetDrivenPos"))[0]
     
     # Parent all groups together
     cmds.parent(self.sdkRotGrp, self.sdkZeroGrp)
     cmds.parent(self.sdkPosGrp, self.sdkRotGrp)
     cmds.parent(self.jawOffset, self.sdkPosGrp)     
     
     # Create a transform to drive our jaw transformations
     self.sdkDriverLoc = cmds.spaceLocator(n=names.nodeName('locator', "{0}{1}SdkDriver".format(self.prefix, self.name), ))[0]
     cmds.delete(cmds.pointConstraint(self.chain[-1], self.sdkDriverLoc))
     cmds.hide(self.sdkDriverLoc)
     self.sdkDriverLocGrp = lib.createNode('transform', names.getDescriptor(self.sdkDriverLoc))
     cmds.delete(cmds.pointConstraint(self.chain[-1], self.sdkDriverLocGrp))
     cmds.parent(self.sdkDriverLoc, self.sdkDriverLocGrp)
     cmds.parent(self.sdkDriverLocGrp, self.grpMisc)
     
     # Set driven keys
     posTxAttrPlug = '{0}.translateZ'.format(self.sdkPosGrp)
     posTzAttrPlug = '{0}.translateX'.format(self.sdkPosGrp)
     rotTxAttrPlug = '{0}.translateZ'.format(self.sdkRotGrp)
     rotTzAttrPlug = '{0}.translateX'.format(self.sdkRotGrp)
     rotRyAttrPlug = '{0}.rotateY'.format(self.sdkRotGrp)
     rotRzAttrPlug = '{0}.rotateZ'.format(self.sdkRotGrp)
     
     # Protraction/Retraction driven keys
     cmds.setDrivenKeyframe(posTxAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(posTzAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     # rename sdk nodes
     posTxSdk = cmds.listConnections(posTxAttrPlug, s=1, d=0, type='animCurve')[0]
     posTxSdk = cmds.rename(posTxSdk, '{0}PosTx_SDK'.format(self.prefix))
     posTzSdk = cmds.listConnections(posTzAttrPlug, s=1, d=0, type='animCurve')[0]
     posTzSdk = cmds.rename(posTzSdk, '{0}PosTz_SDK'.format(self.prefix))
     
     # Protraction driven key
     cmds.setAttr('{0}.translateX'.format(self.sdkDriverLoc), self.sdkDict['protraction']['locValue'])
     cmds.setAttr(posTxAttrPlug, self.sdkDict['protraction']['posTx'])
     cmds.setAttr(posTzAttrPlug, self.sdkDict['protraction']['posTz'])
     cmds.setDrivenKeyframe(posTxAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(posTzAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     
     # Retraction driven key
     cmds.setAttr('{0}.translateX'.format(self.sdkDriverLoc), self.sdkDict['retraction']['locValue'])
     cmds.setAttr(posTxAttrPlug, self.sdkDict['retraction']['posTx'])
     cmds.setAttr(posTzAttrPlug, self.sdkDict['retraction']['posTz'])
     cmds.setDrivenKeyframe(posTxAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(posTzAttrPlug,
         currentDriver='{0}.translateX'.format(self.sdkDriverLoc))
     cmds.setAttr('{0}.translateX'.format(self.sdkDriverLoc), 0)
     
     # Sideways driven keys
     cmds.setDrivenKeyframe(rotRzAttrPlug,
         currentDriver='{0}.translateZ'.format(self.sdkDriverLoc))
     # rename sdk node
     rotRzSdk = cmds.listConnections(rotRzAttrPlug, s=1, d=0, type='animCurve')[0]
     rotRzSdk = cmds.rename(rotRzSdk, '{0}RotRz_SDK'.format(self.prefix))
     
     # Sideways left driven key
     cmds.setAttr('{0}.translateZ'.format(self.sdkDriverLoc), self.sdkDict['sideLeft']['locValue'])
     cmds.setAttr(rotRzAttrPlug, self.sdkDict['sideLeft']['rotRz'])
     cmds.setDrivenKeyframe(rotRzAttrPlug,
         currentDriver='{0}.translateZ'.format(self.sdkDriverLoc))
         
     # Sideways right driven key
     cmds.setAttr('{0}.translateZ'.format(self.sdkDriverLoc), self.sdkDict['sideRight']['locValue'])
     cmds.setAttr(rotRzAttrPlug, self.sdkDict['sideRight']['rotRz'])
     cmds.setDrivenKeyframe(rotRzAttrPlug,
         currentDriver='{0}.translateZ'.format(self.sdkDriverLoc))
     cmds.setAttr('{0}.translateZ'.format(self.sdkDriverLoc), 0)
     
     # Open/Close driven keys
     cmds.setDrivenKeyframe(rotRyAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTxAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTzAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     
     # rename sdk node
     rotRySdk = cmds.listConnections(rotRyAttrPlug, s=1, d=0, type='animCurve')[0]
     rotRySdk = cmds.rename(rotRySdk, '{0}RotRy_SDK'.format(self.prefix))
     rotTxSdk = cmds.listConnections(rotTxAttrPlug, s=1, d=0, type='animCurve')[0]
     rotTxSdk = cmds.rename(rotTxSdk, '{0}RotTx_SDK'.format(self.prefix))
     rotTzSdk = cmds.listConnections(rotTzAttrPlug, s=1, d=0, type='animCurve')[0]
     rotTzSdk = cmds.rename(rotTzSdk, '{0}RotTz_SDK'.format(self.prefix))
         
     # Open driven key
     cmds.setAttr('{0}.translateY'.format(self.sdkDriverLoc), self.sdkDict['open']['locValue'])
     cmds.setAttr(rotRyAttrPlug, self.sdkDict['open']['rotRy'])
     cmds.setAttr(rotTxAttrPlug, self.sdkDict['open']['rotTx'])
     cmds.setAttr(rotTzAttrPlug, self.sdkDict['open']['rotTz'])
     cmds.setDrivenKeyframe(rotRyAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTxAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTzAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
         
     # Close driven key
     cmds.setAttr('{0}.translateY'.format(self.sdkDriverLoc), self.sdkDict['close']['locValue'])
     cmds.setAttr(rotRyAttrPlug, self.sdkDict['close']['rotRy'])
     cmds.setAttr(rotTxAttrPlug, self.sdkDict['close']['rotTx'])
     cmds.setAttr(rotTzAttrPlug, self.sdkDict['close']['rotTz'])
     cmds.setDrivenKeyframe(rotRyAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTxAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setDrivenKeyframe(rotTzAttrPlug,
         currentDriver='{0}.translateY'.format(self.sdkDriverLoc))
     cmds.setAttr('{0}.translateY'.format(self.sdkDriverLoc), 0)
     
     # Set tangents to linear
     cmds.keyTangent(posTxSdk, itt='linear', ott='linear', e=1)
     cmds.keyTangent(posTzSdk, itt='linear', ott='linear', e=1)
     cmds.keyTangent(rotRzSdk, itt='linear', ott='linear', e=1)
     cmds.keyTangent(rotRySdk, itt='linear', ott='linear', e=1)
     cmds.keyTangent(rotTxSdk, itt='linear', ott='linear', e=1)
     cmds.keyTangent(rotTzSdk, itt='linear', ott='linear', e=1)
     
     cmds.connectAttr("{0}.{1}".format(ctlDict['ctl'][0], "translate"), "{0}.{1}".format(self.sdkDriverLoc, "translate"))
     
     # Set transform limits
     cmds.transformLimits(ctlDict['ctl'][0], tx=self.sdkDict['limits']['tx'], etx=[1, 1])
     cmds.transformLimits(ctlDict['ctl'][0], ty=self.sdkDict['limits']['ty'], ety=[1, 1])
     cmds.transformLimits(ctlDict['ctl'][0], tz=self.sdkDict['limits']['tz'], etz=[1, 1])
     
     # ====================
     # Curve shape data
     # ====================
     if self.createCurveShapeDataSet:
         for k in self.ctls:
             if 'ctl' in self.ctls[k]:
                 cmds.sets(self.ctls[k]['ctl'], add=self.curveShapeDataSet)
         self.importCurveShapeData()
    def buildInterp(self, bias=0.0):
        """Build interpolation joint
        
        Note:
            Uses MayaMuscle cSmartConstraint to reliably interpolate a joints rotation between two other joints. Useful
            for skinning tweaks in areas where you need a softer bend, or maintain volume. Changing bias defines how the rotation
            is interpolated between the parent and target joint.
            
            Aim can be x, y, z, -x, -y, -z
        
        Args:
            bias (float): -1 follows parent, 1 follows target
            
        """

        # Prep variables and check for prerequisites
        self.side = names.getSide(self.target)
        self.descriptor = names.getDescriptor(self.target)
        self.basename = "{0}_{1}_{2}".format(self.side, self.descriptor,
                                             "interp")
        if self.target == "":
            raise ValueError(
                "Tried building interpolation joint setup without a target.")

        if not cmds.pluginInfo('MayaMuscle', q=1, l=1):
            try:
                cmds.loadPlugin('MayaMuscle')
            except:
                raise RuntimeError('Unable to load the MayaMuscle plugin!')

        # Setup interp grp
        if not cmds.objExists("rigInterp_grp"):
            rigInterpGrp = cmds.group(n="rigInterp_grp", em=1, w=1)
            if self.parent:
                cmds.parent(rigInterpGrp, self.parent)
            elif cmds.objExists("misc_grp"):
                cmds.parent(rigInterpGrp, "misc_grp")

        # Create hierarchy
        masterGrp = cmds.group(em=1,
                               n=names.addModifier(self.basename, 'grp'),
                               p="rigInterp_grp")
        cmds.delete(cmds.parentConstraint(self.target, masterGrp, mo=0))

        # Create driver joints
        cmds.select(d=1)
        cDrv = cmds.joint(
            n=names.addModifier(self.basename, 'joint', addSuffix='child'))
        cmds.select(d=1)
        pDrv = cmds.joint(
            n=names.addModifier(self.basename, 'joint', addSuffix='parent'))
        cmds.parent(cDrv, pDrv, masterGrp)

        # Identify parent joint
        pJnt = cmds.listRelatives(self.target, p=1, typ="joint")[0]

        # Match dummy joint placements and move rotation to jointOrientation
        cmds.delete(cmds.parentConstraint(pJnt, pDrv, mo=0))
        cmds.delete(cmds.parentConstraint(self.target, cDrv, mo=0))
        cmds.makeIdentity(pDrv, r=1, a=1)
        cmds.makeIdentity(cDrv, r=1, a=1)

        # Create parent constraints
        pParCon = cmds.parentConstraint(pJnt,
                                        pDrv,
                                        mo=0,
                                        n=names.addModifier(
                                            pDrv, 'parentConstraint'))
        cParCon = cmds.parentConstraint(self.target,
                                        cDrv,
                                        mo=0,
                                        n=names.addModifier(
                                            cDrv, 'parentConstraint'))

        # Create the interpolation joint, add helper group structure.
        cmds.select(d=1)
        self.intJnt = cmds.joint(n=names.addModifier(self.basename, 'joint'))
        intOffGrp = cmds.group(em=1,
                               w=1,
                               n=names.addModifier(self.intJnt,
                                                   'grp',
                                                   addSuffix="offset"))
        intConGrp = cmds.group(intOffGrp,
                               w=1,
                               n=names.addModifier(self.intJnt,
                                                   'grp',
                                                   addSuffix="constraint"))
        cmds.delete(cmds.parentConstraint(self.intJnt, intConGrp, mo=0))
        cmds.xform(intOffGrp, ro=(0, 0, 0), t=(0, 0, 0), a=1, ws=1)
        cmds.parent(self.intJnt, intOffGrp)

        # Pretty it up a bit
        cmds.setAttr("{0}.overrideEnabled".format(self.intJnt), 1)
        cmds.setAttr("{0}.radius".format(self.intJnt), 1.5)
        cmds.setAttr("{0}.overrideColor".format(self.intJnt), 17)

        # Create muscle smart constraint and connect it up
        # Note on the cMuscleSmartConstraint:
        # It has a build in trigger with the ability to adjust the bias. So, say you want it to follow the shoulder more
        # at values above 90 degrees, but want it to interpolate 50/50 for all rotation values under that, you can do it
        # by setting a trigger rotation value of 90, and once it hits 90 degrees of rotation, it'll smoothly move bias more
        # towards the shoulder rather than the elbow.

        smartCon = cmds.createNode("cMuscleSmartConstraint",
                                   n=names.addModifier(
                                       self.basename,
                                       'cMuscleSmartConstraint',
                                       addSuffix='constraint'))
        cmds.connectAttr("{0}.worldMatrix".format(pDrv),
                         "{0}.worldMatrixA".format(smartCon))
        cmds.connectAttr("{0}.worldMatrix".format(cDrv),
                         "{0}.worldMatrixB".format(smartCon))
        cmds.connectAttr("{0}.outTranslate".format(smartCon),
                         "{0}.translate".format(self.intJnt))
        cmds.connectAttr("{0}.outRotate".format(smartCon),
                         "{0}.rotate".format(self.intJnt))

        # Set axis based on the aim attribute
        axisList = ["x", "y", "z", "-x", "-y", "-z"]
        axisSet = 0
        for z, i in enumerate(axisList):
            if self.aimAxis == i:
                cmds.setAttr("{0}.axis".format(smartCon), z)
                axisSet = 1

        if not axisSet:
            cmds.setAttr("{0}.axis".format(smartCon), 0)

        # Promote bias attributes etc. to master node for manual adjustments
        cmds.addAttr(masterGrp,
                     ln="customAttributes",
                     nn="__Custom Attributes__",
                     k=1)
        cmds.setAttr("{0}.customAttributes".format(masterGrp), l=1)
        cmds.addAttr(
            masterGrp,
            ln="axis",
        )
        cmds.addAttr(masterGrp, ln="trigger", k=1, min=0, max=180)
        cmds.addAttr(masterGrp, ln="bias", k=1, min=-1, max=1)
        cmds.addAttr(masterGrp, ln="biasAdjust", k=1, min=-2, max=2)

        cmds.connectAttr("{0}.trigger".format(masterGrp),
                         "{0}.triggerMin".format(smartCon))
        cmds.connectAttr("{0}.bias".format(masterGrp),
                         "{0}.bias".format(smartCon))
        cmds.connectAttr("{0}.biasAdjust".format(masterGrp),
                         "{0}.biasAdjust".format(smartCon))

        # Clean up
        cmds.parent(intConGrp, masterGrp)
        cmds.setAttr("{0}.bias".format(masterGrp), bias)
        cmds.setAttr("{0}.inheritsTransform".format(masterGrp), 0)

        for i in [cDrv, pDrv]:
            cmds.setAttr("{0}.v".format(i), 0)

        attrList = ["sx", "sy", "sz"]
        for attr in attrList:
            cmds.setAttr("{0}.{1}".format(self.intJnt, attr), l=1)
    def buildTwist(self, reverse=0, noflip=0, stretch=0):
        """Builds a twist joint setup.
        
        Note:
            Requires at least 3 joints to build (ie. shoulder - elbow - wrist).
        
        Example:
            test = HelperJoints()
            test.target = "joint2"
            test._buildTwist(noflip=1)
        
        Args:
            reverse (bool): Defines whether we're extracting the twist down the chain or up the chain. 
                In a general sense, for a shoulder, reverse should be 0, for an ankle, reverse should be 1.
            noflip (bool): Sets the constraints to noflip mode, allowing 360 degree rotation
            stretch (bool): Toggles whether or not the joint length should be continuesly sampled to "stretch" 
                twist joints along with it whatever it's build inbetween.
            
        """
        # Default args
        if self.target == "":
            raise ValueError(
                "Tried building twist joint setup without a target (self.target)."
            )
        if self.val == 0:
            raise ValueError(
                "Tried building twist joint with specified amount (self.val) being 0 (no twist joints)"
            )
        self.side = names.getSide(self.target)
        self.descriptor = names.getDescriptor(self.target)
        if self.side:
            self.basename = "{0}_{1}_{2}".format(self.side, self.descriptor,
                                                 "twist")
        else:
            self.basename = "{0}_{1}".format(self.descriptor, "twist")

        # Setup twist grp
        if not cmds.objExists("rigTwist_grp"):
            rigTwistGrp = cmds.group(n="rigTwist_grp", em=1, w=1)
            if self.parent:
                cmds.parent(rigTwistGrp, self.parent)
            elif cmds.objExists("misc_grp"):
                cmds.parent(rigTwistGrp, "misc_grp")

        # Check if matrix nodes are loaded
        if not cmds.pluginInfo('matrixNodes', q=1, l=1):
            try:
                cmds.loadPlugin('matrixNodes')
            except:
                raise RuntimeError('Unable to load the matrixNodes plugin!')

        # Could potentially remove type limitation from this section if needed in the future.
        if self.childTarget == "":
            self.childTarget = cmds.listRelatives(self.target,
                                                  c=1,
                                                  typ="joint")[0]
        if self.parentTarget == "":
            self.parentTarget = cmds.listRelatives(self.target,
                                                   p=1,
                                                   typ="joint")[0]
        if self.jntLen == 0:
            lenFound = 0
            for i in ["x", "y", "z"]:
                if self.aimAxis == i:
                    self.jntLen = cmds.getAttr("{0}.t{1}".format(
                        self.target, i))
                    lenFound = 1
            if not lenFound and not self.jntLen:
                raise ValueError(
                    "Joint length was not specified and the length could not be retrieved automatically"
                )

        masterGrp = cmds.group(n=names.addModifier(self.basename,
                                                   'grp',
                                                   addSuffix='Master'),
                               em=1,
                               p="rigTwist_grp")
        cmds.setAttr("{0}.inheritsTransform".format(masterGrp), 0)

        # Create dummy setup
        dummyTarget = cmds.duplicate(self.target,
                                     po=1,
                                     n=names.addModifier(
                                         self.basename,
                                         'joint',
                                         addSuffix='DummyTarget'))[0]
        dummyParent = cmds.duplicate(self.parentTarget,
                                     po=1,
                                     n=names.addModifier(
                                         self.basename,
                                         'joint',
                                         addSuffix='DummyParent'))[0]
        dummyChild = cmds.duplicate(self.childTarget,
                                    po=1,
                                    n=names.addModifier(
                                        self.basename,
                                        'joint',
                                        addSuffix='DummyChild'))[0]

        cmds.parent(dummyChild, dummyTarget)
        cmds.parent(dummyTarget, dummyParent)
        cmds.parent(dummyParent, masterGrp)

        matchList = [self.target, self.parentTarget, self.childTarget]

        for z, i in enumerate([dummyTarget, dummyParent, dummyChild]):
            cmds.parentConstraint(matchList[z],
                                  i,
                                  n=names.addModifier(i, 'parentConstraint'),
                                  mo=0)

        # Create slave joints
        slaveA = cmds.duplicate(dummyTarget,
                                po=1,
                                n=names.addModifier(self.basename,
                                                    'joint',
                                                    addSuffix='SlaveA'))[0]
        if reverse:
            slaveB = cmds.duplicate(dummyParent,
                                    po=1,
                                    n=names.addModifier(self.basename,
                                                        'joint',
                                                        addSuffix='SlaveB'))[0]
        else:
            slaveB = cmds.duplicate(dummyChild,
                                    po=1,
                                    n=names.addModifier(self.basename,
                                                        'joint',
                                                        addSuffix='SlaveB'))[0]
        cmds.parent(slaveB, slaveA)

        # Redundant parenting, just to be sure
        if not cmds.listRelatives(slaveA, p=1, typ="joint")[0] == dummyParent:
            cmds.parent(slaveA, dummyParent)

        # Create IK to remove the main axis
        rpIK, rpEE = cmds.ikHandle(n=names.addModifier(self.basename,
                                                       'ikHandle',
                                                       addSuffix='Ext'),
                                   sj=slaveA,
                                   ee=slaveB)
        rpEE = cmds.rename(
            rpEE,
            names.addModifier(self.basename, 'endEffector', addSuffix='Ext'))
        cmds.parent(rpIK, dummyTarget)
        cmds.setAttr("{0}.poleVectorX".format(rpIK), 0)
        cmds.setAttr("{0}.poleVectorY".format(rpIK), 0)
        cmds.setAttr("{0}.poleVectorZ".format(rpIK), 0)

        # Create space locators that'll help store the current twist value (and visualize for debugging)
        twistValA = cmds.spaceLocator(
            n=names.addModifier(self.basename, 'locator', addSuffix='A'))[0]
        twistValB = cmds.spaceLocator(
            n=names.addModifier(self.basename, 'locator', addSuffix='B'))[0]
        cmds.parent(twistValA, twistValB, dummyTarget)
        attrList = [".tx", ".ty", ".tz", ".rx", ".ry", ".rz"]
        for attr in attrList:
            cmds.setAttr(twistValA + attr, 0)
            cmds.setAttr(twistValB + attr, 0)

        valAOri = cmds.orientConstraint(slaveA,
                                        twistValA,
                                        n=names.addModifier(self.basename,
                                                            'orientConstraint',
                                                            addSuffix='A'))[0]
        valBOri = cmds.orientConstraint(slaveA,
                                        dummyTarget,
                                        twistValB,
                                        n=names.addModifier(self.basename,
                                                            'orientConstraint',
                                                            addSuffix='B'))[0]

        if noflip:
            cmds.setAttr("{0}.interpType".format(valAOri), 0)
            cmds.setAttr("{0}.interpType".format(valBOri), 0)

        # Create transform for easy attribute access and prep
        twistGrp = cmds.group(n=names.addModifier(self.basename,
                                                  'grp',
                                                  addSuffix='Ext'),
                              em=1,
                              w=1)
        cmds.parent(twistGrp, masterGrp)
        attrList = [
            ".tx", ".ty", ".tz", ".rx", ".ry", ".rz", ".sx", ".sy", ".sz", ".v"
        ]
        for attr in attrList:
            cmds.setAttr(twistGrp + attr, l=1, k=0)

        cmds.addAttr(twistGrp, ln="twistValA", k=1)
        cmds.addAttr(twistGrp, ln="twistValB", k=1)

        cmds.connectAttr("{0}.rx".format(twistValA),
                         "{0}.twistValA".format(twistGrp))

        valBmdl = cmds.createNode("multDoubleLinear",
                                  n=names.addModifier(self.basename,
                                                      'multDoubleLinear',
                                                      addSuffix='ValueB'))
        cmds.setAttr("{0}.input2".format(valBmdl), 2)
        cmds.connectAttr("{0}.rx".format(twistValB),
                         "{0}.input1".format(valBmdl))
        cmds.connectAttr("{0}.output".format(valBmdl),
                         "{0}.twistValB".format(twistGrp))

        # Create a group for the twist joints to get a better ui result
        jntGrp = cmds.group(n=names.addModifier(self.basename,
                                                'grp',
                                                addSuffix='Joints'),
                            em=1,
                            p=masterGrp)
        cmds.parentConstraint(dummyParent,
                              jntGrp,
                              mo=0,
                              n=names.addModifier(self.basename,
                                                  'parentConstraint',
                                                  addSuffix='Joints'))

        # Get the placement values for the twist joints
        val = self.val + 1
        jntPlacement = []

        if self.aimAxis.lower() == "y":
            tr = "ty"
        elif self.aimAxis.lower() == "z":
            tr = "tz"
        else:
            tr = "tx"
        self.twistJoints = list()
        for i in range(val):
            i += 1
            if not i == val:
                div = float(i) / float(val)
                jntPlacement = float("{0:.2f}".format(self.jntLen * div))
                curJoint = str(i)

                # Create, place and connect twist joints based on extracted information
                jnt = cmds.duplicate(dummyParent,
                                     po=1,
                                     n=names.addModifier(
                                         self.basename,
                                         'joint',
                                         addSuffix=curJoint))[0]
                cmds.parent(jnt, jntGrp)

                for i in ["jointOrientX", "jointOrientY", "jointOrientZ"]:
                    cmds.setAttr("{0}.{1}".format(jnt, i), 0)

                if stretch:
                    stretchMdl = cmds.createNode(
                        "multDoubleLinear",
                        n=names.addModifier(self.basename,
                                            'multDoubleLinear',
                                            addSuffix=curJoint + "Stretch"))
                    cmds.setAttr("{0}.input2".format(stretchMdl), div)
                    cmds.connectAttr("{0}.{1}".format(dummyTarget, tr),
                                     "{0}.input1".format(stretchMdl))
                    cmds.connectAttr("{0}.output".format(stretchMdl),
                                     "{0}.{1}".format(jnt, tr))

                else:
                    cmds.setAttr("{0}.{1}".format(jnt, tr), jntPlacement)

                twistValmdl = cmds.createNode("multDoubleLinear",
                                              n=names.addModifier(
                                                  self.basename,
                                                  'multDoubleLinear',
                                                  addSuffix=curJoint))
                cmds.setAttr("{0}.input2".format(twistValmdl), (div * -1))
                if noflip:
                    cmds.connectAttr("{0}.twistValB".format(twistGrp),
                                     "{0}.input1".format(twistValmdl))
                else:
                    cmds.connectAttr("{0}.twistValA".format(twistGrp),
                                     "{0}.input1".format(twistValmdl))
                cmds.connectAttr("{0}.output".format(twistValmdl),
                                 "{0}.rx".format(jnt))

                # Pretty it up a bit
                cmds.setAttr("{0}.overrideEnabled".format(jnt), 1)
                cmds.setAttr("{0}.radius".format(jnt), 0.5)
                cmds.setAttr("{0}.overrideColor".format(jnt), 12)

                # Lock values
                attrList = ["tx", "ty", "tz", "rx", "ry", "rz"]
                for attr in attrList:
                    cmds.setAttr("{0}.{1}".format(jnt, attr), l=1)

                # Assign joints to variable
                self.twistJoints = self.twistJoints + [jnt]

        # Clean up
        cmds.setAttr("{0}.v".format(dummyParent), 0)