Ejemplo n.º 1
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        if not spaceName:
            spaceName = pdil.simpleName(target) + '_altRot'

        trueTarget = group(em=True,
                           name=pdil.simpleName(target, '{0}_split'),
                           p=spaceContainer)

        xform(trueTarget, ws=1, t=xform(control, q=True, ws=True, t=True))

        trans = parentConstraint(target,
                                 trueTarget,
                                 mo=True,
                                 sr=['x', 'y', 'z'])

        # An explicit target can be given, otherwise it ends up being the main controller in effect.
        if rotateTarget:
            rot = parentConstraint(rotateTarget,
                                   trueTarget,
                                   mo=True,
                                   st=['x', 'y', 'z'])
            trans.addAttr('transTarget', at='bool', dv=True)
            rot.addAttr('rotTarget', at='bool', dv=True)

        return trueTarget, spaceName
Ejemplo n.º 2
0
 def build(cls, target, spaceName, spaceContainer, constraintWeights,
           control, space):
     '''  In this mode, target is a list of targets and constraintWeights is an optional list of weights
     Args:
         target: List of targets, but the first is the "fallback" space use to move the whole system
         constraintWegiths: Optional dict of weights for the constraints {'points': [p1, p2, p3], 'orients': [o1, o2, o3]}
     '''
     if not spaceName:
         spaceName = cls.DEFAULT_SPACENAME
     wrapper = group(em=True,
                     name=pdil.simpleName(control, '{0}_wrapMultiPR'),
                     p=spaceContainer)
     #pdil.dagObj.matchTo(wrapper, PyNode(target[0]))  # Not sure if this actually matters
     parentConstraint(target[0], wrapper)
     trueTarget = group(em=True,
                        name=pdil.simpleName(control, '{0}_multiPR'),
                        p=wrapper)
     pdil.dagObj.matchTo(
         trueTarget,
         control)  # It's actually important so this can follow correctly.
     if constraintWeights:
         points = constraintWeights.get('points', [1.0] * (len(target) - 1))
         orients = constraintWeights.get('orients',
                                         [1.0] * (len(target) - 1))
     else:
         points = [1.0] * (len(target) - 1)
         orients = [1.0] * (len(target) - 1)
     for t, p, o in zip(target[1:], points, orients):
         oc = orientConstraint(t, trueTarget, w=o, mo=True)
         pointConstraint(t, trueTarget, w=p, mo=True)
     oc.interpType.set(2)
     return trueTarget, spaceName
Ejemplo n.º 3
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        #  In this mode, target is a list of targets and rotateTarget is an optional list of weights
        if not spaceName:
            #spaceName = pdil.simpleName(target) + '_' + pdil.simpleName(rotateTarget) + '_follow'
            spaceName = 'multi_parent'

        trueTarget = group(em=True,
                           name=pdil.simpleName(target[0], '{0}_multiParent'),
                           p=spaceContainer)

        pdil.dagObj.matchTo(
            trueTarget,
            control)  # It's actually important so this can follow correctly.

        proxies = []
        for t in target:
            d = duplicate(trueTarget)[0]
            d.rename(d.name() + '_' + pdil.simpleName(t))
            parentConstraint(t, d, mo=True)
            proxies.append(d)

        constraint = parentConstraint(proxies, trueTarget, mo=True)
        constraint.interpType.set(
            2)  # Shorted rotation is probably the most stable

        if rotateTarget:
            attrs = constraint.getWeightAliasList()
            for v, attr in zip(rotateTarget, attrs):
                attr.set(v)

        return trueTarget, spaceName
Ejemplo n.º 4
0
def dupChain(start, end, nameFormat='{0}_dup'):
    '''
    Creates a duplicate chain, pruned of all branches and children.  Can handle
    same joints and start and end.
    
    :param string nameFormat: The str.format used on the duped chain
    
    '''

    chain = getChain(start, end)

    assert chain, '{0} and {1} are not in the same hierarchy, dupChain() failed'.format(
        start, end)

    dup = duplicate(start)[0]

    if start != end:
        child = findChild(dup, pdil.simpleName(end))
        assert child, 'dupChain failed to find duped child {0} in {1}'.format(
            end, start)
        prune(dup, child)
    else:
        child = dup

    dupedChain = getChain(dup, child)

    ends = dupedChain[-1].getChildren(type='transform')
    if ends:
        delete(ends)

    for src, d in zip(chain, dupedChain):
        dupName = pdil.simpleName(src, nameFormat)
        d.rename(dupName)
    return dupedChain
Ejemplo n.º 5
0
def getGroup(modeName, main=None, checkOnly=False):
    ''' Returns the group that contains the space's proxy target, i.e. the node the zero space is actually constrained to.
    
    Args:
        modeName: str = One of the target modes, ex: 'PARENT', 'POINT_ORIENT', etc.
        main: PyNode = The main controller in case the scene has multiple.
        checkOnly: bool = If True, won't build the space and returns None if not found.
    '''
    modeName = toCamel(modeName)
    if not main:
        main = find.mainGroup()

    for child in main.listRelatives():
        if pdil.simpleName(child) == '__spaces__':
            spaceContainer = child
            break
    else:
        spaceContainer = group(em=True, n='__spaces__', p=main)
        hide(spaceContainer)

    for child in spaceContainer.listRelatives():
        if pdil.simpleName(child) == modeName:
            spaceGroup = child
            break
    else:
        if checkOnly:
            return None
        else:
            spaceGroup = group(em=True, name=modeName, p=spaceContainer)

    return spaceGroup
Ejemplo n.º 6
0
def pointer(parent, child):
    '''
    Makes proxy joints for two TempJoints so the hierarchical relationship can be drawn.
    
    ..  todo:: I think the actual connection logic might move to BPJoint.setParent
    '''

    assert type(parent).__name__ == type(
        child).__name__ == 'BPJoint', 'Both must be TempJoints'

    if not parent.attr('children').isConnectedTo(child.attr('parent')):
        parent.attr('children') >> child.attr('parent')

    proxyRadius = _DEFAULT_PROXY_RADIUS
    try:
        proxyRadius = parent.parent.proxy.radius.get()
    except:
        pass
    grp = getProxyGroup()

    if not child.proxy:
        makeProxy(child, grp, child.radius.get() * _DEFAULT_PROXY_RADIUS)

    if not parent.proxy:
        makeProxy(parent, grp, parent.radius.get() * _DEFAULT_PROXY_RADIUS)

    # If card parentage is established, manage vis
    if parent.cardCon.node() != child.cardCon.node():
        parentCardName = simpleName(parent.cardCon.node())
        childCardName = simpleName(child.cardCon.node())

        child.proxy.setParent(grp)

        linkStart = joint(grp)
        linkStart.radius.set(parent.radius.get() * proxyRadius)
        linkEnd = joint(linkStart)
        linkEnd.radius.set(child.radius.get() * proxyRadius)

        pointConstraint(parent, linkStart)
        pointConstraint(child, linkEnd)

        pdil.math.multiply(parent.cardCon.node().v,
                           child.cardCon.node().v) >> linkStart.v

        if not child.cardCon.node().v.isConnectedTo(child.v):
            child.cardCon.node().v >> child.v

        child.v >> child.proxy.v

        _clearLink(child.proxy)
        _recordLink(child.proxy, linkStart)

        child.proxy.rename(childCardName + '_proxy')
        linkStart.rename(parentCardName + '_' + childCardName + '_link')

    else:
        child.proxy.setParent(parent.proxy)
Ejemplo n.º 7
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        if not spaceName:
            spaceName = pdil.simpleName(target) + '_' + pdil.simpleName(
                rotateTarget) + '_follow'
        trueTarget = group(em=True,
                           name=pdil.simpleName(target, '{0}_dualFollow'),
                           p=spaceContainer)
        pdil.dagObj.matchTo(trueTarget, control)
        pointConstraint(target, rotateTarget, trueTarget, mo=True)
        orientConstraint(target, rotateTarget, trueTarget, mo=True)

        return trueTarget, spaceName
Ejemplo n.º 8
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        if not spaceName:
            spaceName = pdil.simpleName(target) + '_pos'

        for child in spaceContainer.listRelatives():
            if target in pointConstraint(child, q=True, tl=True):
                trueTarget = child
                break
        else:
            trueTarget = group(em=True,
                               name=pdil.simpleName(target, '{0}_posOnly'),
                               p=spaceContainer)
            pointConstraint(target, trueTarget)

        return trueTarget, spaceName
Ejemplo n.º 9
0
def rootBone(nodes=None):
    '''
    Returns the root bone, trying to account for case and namespaces or None if
    not found.  `make` should be either 'root' or 'weaponRoot', specifying which
    to make (always top level) if a root is not found.
    
    Can be given a list of nodes to search for the root,
    '''

    names = [config._settings['root_name']]

    if not nodes:
        # Check if any exist top level first
        top = ls(assemblies=True)
        for name in names:
            if name in top:
                return PyNode('|' + name)

    # See if there is a top level obj in a namespace named b_root of any casing.
    searchNodes = nodes if nodes else ls(assemblies=True)
    nodes = [obj for obj in searchNodes if simpleName(obj).lower() in names]
    if len(nodes) == 1:
        return nodes[0]

    # Then check if any exist at all (which will fail if several exist in separate groups).
    for name in names:
        if objExists(name):
            return PyNode(name)

    return None
Ejemplo n.º 10
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        if not spaceName:
            spaceName = pdil.simpleName(target) + '_' + pdil.simpleName(
                rotateTarget) + '_parent'

        trueTarget = group(em=True,
                           name=pdil.simpleName(target, '{0}_dualParent'),
                           p=spaceContainer)
        pdil.dagObj.matchTo(trueTarget, control)

        # parentConstraint doesn't actually respect maintainOffset regarding rotation
        parentConstraint([target, rotateTarget],
                         trueTarget,
                         mo=True,
                         sr=['x', 'y', 'z'])
        orientConstraint([target, rotateTarget], trueTarget, mo=True)

        return trueTarget, spaceName
Ejemplo n.º 11
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        if not spaceName:
            spaceName = pdil.simpleName(target) + '_rot'

        trueTarget = group(em=True,
                           name=pdil.simpleName(target, '{0}_rotOnly'),
                           p=spaceContainer)

        xform(trueTarget, ws=1, t=xform(control, q=True, ws=True, t=True))

        # parentConstraint translate to it's parent so it remains in position and lets us do a full parentConstraint later
        parentConstraint(space.getParent(),
                         trueTarget,
                         mo=True,
                         sr=['x', 'y', 'z'])

        parentConstraint(target, trueTarget, mo=True, st=['x', 'y', 'z'])

        return trueTarget, spaceName
Ejemplo n.º 12
0
def trimName(jnt):
    '''
    Given an joint, return its simple name without b_ or rig_ if those prefixes exist.
    '''
    name = pdil.simpleName(jnt)
    prefix = config._settings['joint_prefix']
    if name.startswith(prefix):
        return name[len(prefix):]

    return name
Ejemplo n.º 13
0
    def jointListerAddRow(self, ctr, tempJoint, name, card, parentCard):
        index = next(ctr)

        jointName = pdil.shortName(tempJoint)
        self.setItem(index, self.JOINT_LISTER_NAME, Cell(jointName))

        cb = Cell(checked=tempJoint.isHelper)
        self.setItem(index, self.JOINT_LISTER_HELPER, cb)

        self.setItem(index, self.JOINT_LISTER_OUTPUT, Cell(name))

        cb = Cell(checked=tempJoint.displayHandle.get())
        self.setItem(index, self.JOINT_LISTER_HANDLES, cb)

        # --- Orient ---
        orientText = ''
        if tempJoint.customOrient:

            if tempJoint.customOrient == tempJoint.card:
                orientText = '-as card-'
            elif tempJoint.customOrient == tempJoint:
                orientText = '-as proxy-'
            else:
                orientText = 'custom:' + pdil.shortName(tempJoint.customOrient)

        elif isinstance(tempJoint.orientTarget, basestring):
            orientText = tempJoint.orientTarget
        elif tempJoint.orientTarget:
            orientText = pdil.shortName(tempJoint.orientTarget)
        self.setItem(index, self.JOINT_LISTER_ORIENT, Cell(orientText))

        # --- parent ---

        if tempJoint.parent:
            # Technically this will fail if there is a helper also has a child (which is just fine, just not useful)
            outputMap = tempJoint.parent.card.getOutputMap(includeHelpers=True)
            if tempJoint.info.get('options', {}).get('mirroredSide'):
                parentName = outputMap[tempJoint.parent][1]
            else:
                parentName = outputMap[tempJoint.parent][0]

            if not parentName:  # This being empty means the parent is a helper
                parentName = '!helper! ' + pdil.simpleName(tempJoint.parent)

        elif tempJoint.extraNode[0]:
            outputMap = tempJoint.extraNode[0].card.getOutputMap(
                includeHelpers=False)
            parentName = outputMap[tempJoint.extraNode[0]][1]

        else:
            parentName = ''

        self.setItem(index, self.JOINT_LISTER_CHILDOF, Cell(parentName))
Ejemplo n.º 14
0
    def convertNodesToNames(self):
        '''
        Returns an json serializable version of the current preset file.
        '''
        convertedMaster = collections.OrderedDict()
        for presetName, preset in self.profiles.items():
            converted = collections.OrderedDict()
            for ctrl, space in preset.items():
                ctrlName = pdil.simpleName(ctrl) if not isinstance(
                    ctrl, basestring) else ctrl

                converted[ctrlName] = space
            convertedMaster[presetName] = converted

        return convertedMaster
Ejemplo n.º 15
0
def addUserDriven(control, spaceName):
    targetName = pdil.simpleName(control) + '_' + spaceName
    userGroup = common.getGroup(common.Mode.USER)

    if targetName in userGroup.listRelatives(type='transform'):
        warning('This space/target already exists')
        return

    trueTarget = group(em=True, name=targetName)
    trueTarget.setParent(userGroup)
    pdil.dagObj.matchTo(trueTarget, control)
    pdil.dagObj.align(trueTarget, make=True)

    add(control, trueTarget, spaceName, mode=common.Mode.USER)

    return trueTarget
Ejemplo n.º 16
0
    def addRow(self, row, ctrl, space):
        name = pdil.simpleName(ctrl)
        selectButton = QtWidgets.QPushButton(name)
        selectButton.clicked.connect(partial(select, ctrl))

        spaceChooser = QtWidgets.QComboBox()
        spaceChooser.addItems(fossil.space.getNames(ctrl) + [ACTIVATE_KEY])
        spaceChooser.setCurrentText(space)
        spaceChooser.currentTextChanged.connect(partial(self.setSpace, ctrl))

        removeButton = QtWidgets.QPushButton('X')
        removeButton.clicked.connect(partial(self.removeControl, ctrl))

        #self.ui.profileTable.setItem(row, 0, QtWidgets.QTableWidgetItem(ctrl.name) )
        self.ui.profileTable.setCellWidget(row, 0, selectButton)
        self.ui.profileTable.setCellWidget(row, 1, spaceChooser)
        self.ui.profileTable.setCellWidget(row, 2, removeButton)
Ejemplo n.º 17
0
    def build(target, spaceName, spaceContainer, rotateTarget, control, space):
        #  In this mode, target is a list of targets and rotateTarget is an optional list of weights
        if not spaceName:
            spaceName = 'multi_orient'

        trueTarget = group(em=True,
                           name=pdil.simpleName(control, '{0}_multiOrient'),
                           p=spaceContainer)

        pdil.dagObj.matchTo(
            trueTarget,
            control)  # It's actually important so this can follow correctly.

        proxies = []
        for t in target:
            d = duplicate(trueTarget)[0]
            orientConstraint(t, d, mo=True)
            proxies.append(d)

            # Turn of inheritTransform so we can place it under the trueTarget to keep things tidy w/o cycle errors
            d.inheritsTransform.set(0)
            d.setParent(trueTarget)

        #constraint = orientConstraint( proxies, trueTarget, mo=True )
        #constraint.interpType.set(2)  # Shorted rotation is probably the most stable
        constraint = parentConstraint(proxies,
                                      trueTarget,
                                      mo=True,
                                      st=['x', 'y', 'z'])
        constraint.addAttr('rotTarget', at='bool', dv=True)
        constraint.interpType.set(2)

        if rotateTarget:
            attrs = constraint.getWeightAliasList()
            for v, attr in zip(rotateTarget, attrs):
                attr.set(v)

        # parentConstraint translate to it's parent so it remains in position and lets us do a full parentConstraint later
        parentConstraint(space.getParent(),
                         trueTarget,
                         mo=True,
                         sr=['x', 'y', 'z'])

        return trueTarget, spaceName
Ejemplo n.º 18
0
    def __init__(self, card):

        self.card = card
        rigData = card.rigData
        name = pdil.simpleName(card)
        #head, repeat, tail = util.parse(card.nameInfo.get())
        names = rigData.get('nameInfo', {'head': [], 'repeat': '', 'tail': []})

        head = ' '.join(names.get('head', []))
        repeat = names.get('repeat', '')
        tail = ' '.join(names.get('tail', []))

        side = rigData.get('mirrorCode', '')

        QtWidgets.QTreeWidgetItem.__init__(
            self, [name, '', '', head, repeat, tail, '', side])

        self.setCheckState(
            1, Qt.Checked if card.visibility.get() else Qt.Unchecked)
        self.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable
                      | Qt.ItemIsSelectable)
Ejemplo n.º 19
0
    def setProfile(self, profileName):
        log.debug('set profile ' + profileName)
        self.clearControlSpaces()

        if not profileName:
            return

        self.curProfile = collections.OrderedDict()

        # Get controls from the character chooser, defaulting to all
        index = self.ui.characterChooser.currentIndex(
        ) - 1  # First item is blank so offset by 1
        if index >= 0:
            allControls = fossil.find.controllers(
                main=self.mainControllers[index])
        else:
            allControls = fossil.find.controllers()

        nameMap = {pdil.simpleName(ctrl): ctrl for ctrl in allControls}

        items = self.profiles[profileName].items()
        self.ui.profileTable.setRowCount(len(items))

        for i, (ctrlName, space) in enumerate(items):

            if ctrlName in nameMap:
                self.curProfile[nameMap[ctrlName]] = space
                self.addRow(i, nameMap[ctrlName], space)

            elif isinstance(ctrlName, PyNode):
                self.curProfile[ctrlName] = space
                self.addRow(i, ctrlName, space)

            else:
                self.curProfile[ctrlName] = space
                self.addEmpty(i, ctrlName, space)

        self.profiles[profileName] = self.curProfile

        self.enableProfileGui(True)
Ejemplo n.º 20
0
    def setOrientTarget(self, row, bpJoint, newTarget):

        if newTarget is None:
            bpJoint.orientTarget = None
            self.clearCustomOrient(bpJoint)
            self.item(row, self.JOINT_LISTER_ORIENT).setText('')

        elif newTarget == '-as card-':
            bpJoint.orientTarget = None
            bpJoint.customOrient = bpJoint.card
            self.item(row, self.JOINT_LISTER_ORIENT).setText('-as card-')

        elif newTarget == '-as proxy-':
            bpJoint.orientTarget = None
            bpJoint.customOrient = bpJoint
            self.item(row, self.JOINT_LISTER_ORIENT).setText('-as proxy-')

        elif newTarget == '-world-':
            '''
            ..  todo:: Make a pass to fix the old stuff then remove the code
                rebuilding the attr if needed.
            '''

            # Since this used to be a message attr, rebuild it (as a string)
            if bpJoint.hasAttr(
                    'orientTargetJnt') and bpJoint.orientTargetJnt.type(
                    ) == 'message':
                bpJoint.deleteAttr('orientTargetJnt')

            bpJoint.orientTarget = '-world-'
            #textFieldButtonGrp(field, e=True, tx='-world-')
            self.item(row, self.JOINT_LISTER_ORIENT).setText('-world-')
            self.clearCustomOrient(bpJoint)

        else:
            self.item(row, self.JOINT_LISTER_ORIENT).setText(
                pdil.simpleName(newTarget))
            bpJoint.orientTarget = newTarget
            bpJoint.customOrient = None
Ejemplo n.º 21
0
def parentGroup(target):
    '''
    Returns a group that is constrained to the parent of the target.
    Used to allow control hierarchies to live elsewhere.
    
    ..  todo::
        Get rid of parentProxy, which is dumb
    '''

    name = pdil.simpleName(target, '{0}_Proxy')
    grp = group(em=True, name=name)

    info = jsonGet(target, 'fossilAccessoryInfo')

    if info.get('parent'):
        parentConstraint(info['parent'], grp, mo=False)

    # Don't constrain top level nodes since they need to follow main, not b_Root
    elif target.getParent() != node.getTrueRoot():
        parentConstraint(target.getParent(), grp, mo=False)

    return grp
Ejemplo n.º 22
0
def customOrient(bpJoint):
    newNodes = importFile(os.path.dirname(__file__) + '/Axis.ma',
                          rnn=True,
                          renameAll=True)
    transform = ls(newNodes, type='transform')[0]
    masterGroup = proxyskel.masterGroup()
    for child in masterGroup.listRelatives():
        if child.name() == 'customOrients':
            customGroup = child
            break
    else:
        customGroup = group(n='customOrients', p=masterGroup, em=True)

    transform.setParent(customGroup)
    transform.scale.set(3, 3, 3)

    transform.t.setKeyable(False)
    bpJoint.customOrient = transform
    pointConstraint(bpJoint, transform)
    transform.t.lock()

    transform.rename(pdil.simpleName(bpJoint, 'orient_{0}'))
Ejemplo n.º 23
0
def mainGroup(nodePool=None, fromControl=None):
    ''' Returns the main group, intended for building the rig where a single main group is assumed, unless nodePool is used.
    
    Args:
        nodePool: A list of possible nodes (like the results of importing/referencing)
        fromControl: Find the mainGroup in the hierarchy of this control
        
    TODO: At some point, (2023?) remove all the 'main' name assumptions and only use the attribute.
    '''

    if fromControl:
        path = fromControl.fullPath().split('|')[1:]
        for i, name in enumerate(path):
            if attributeQuery(config.FOSSIL_MAIN_CONTROL, ex=True, node=name):
                return PyNode('|' + '|'.join(path[:i + 1]))

    if nodePool:
        for n in nodePool:
            if n.hasAttr(
                    config.FOSSIL_MAIN_CONTROL) or simpleName(n) == 'main':
                return n

    if objExists('|main'):
        return PyNode('|main')
    else:
        # A pymel bug is sometimes returning duplicate nodes
        main = list(
            set([obj for obj in ls('main', r=True) if not obj.getParent()]))
        if len(main) == 1:
            return main[0]

    # No "main" ojbect was found, looking for tags
    plugs = ls('*.' + config.FOSSIL_MAIN_CONTROL)
    if plugs:
        return plugs[0].node()

    return None
Ejemplo n.º 24
0
def buildFreeform(joints,
                  translatable=False,
                  mirroredTranslate=False,
                  scalable=False,
                  groupName='',
                  controlSpec={}):
    '''
    Make an FK chain between the given joints.
    
    :param PyNode start: Start of joint chain
    :param PyNode end: End of chain
    :param bool translatable: Default=False
    :param bool mirroredTranslate: If true, translations will be flipped
    :param dict controlSpec: Override default control details here.  Only has 'main'.
    
    ..  todo::
        I think I want to control spec housed elsewhere for easier accessibility.
    
    '''

    # Make a top level section for each lead joint in the subHierarchy
    #topLevel = [j for j in joints if j.getParent() not in joints]

    topContainer = group(n=pdil.simpleName(joints[0], '{0}_Freeform'),
                         p=node.mainGroup(),
                         em=True)

    #top = container
    #leadOrient, leadPoint = None, None

    controls = []

    done = {}
    for j in joints:
        done[j] = False

    for j in joints:

        ctrl = controllerShape.build(
            util.trimName(j) + "_ctrl",
            controlSpec['main'],
            type=controllerShape.ControlType.TRANSLATE
            if translatable else controllerShape.ControlType.ROTATE)
        controls.append(ctrl)
        pdil.dagObj.matchTo(ctrl, j)

        constraints = util.constrainTo(j, ctrl, includeScale=scalable)

        space = pdil.dagObj.zero(ctrl)
        if mirroredTranslate:
            space.s.set(-1, -1, -1)

        done[j] = (ctrl, space)

        # Lock unneeded transforms
        if not translatable:
            pdil.dagObj.lock(ctrl, 't')

        if not scalable:
            pdil.dagObj.lock(ctrl, 's')
        else:
            # Preserving scaling symmetry if translations are mirrored
            if mirroredTranslate:
                constraints[2].node().offsetZ.set(-1)

    # Parenting the controllers is challenging since they could occur in any order
    for jnt, (ctrl, space) in done.items():
        if jnt.getParent() in done:
            space.setParent(done[jnt.getParent()][0])
        else:
            container = util.parentGroup(jnt)
            container.setParent(topContainer)
            container.rename(util.trimName(jnt) + '_fkChain')

            space.setParent(container)

    # A leader must be choosen, so just use the order they were built in
    ctrl = pdil.nodeApi.RigController.convert(controls[0])
    log.debug('The leader is {}'.format(ctrl))
    for i, c in enumerate(controls[1:]):
        ctrl.subControl[str(i)] = c
        log.debug('Adding {} {}'.format(i, c))

    ctrl.container = topContainer

    return ctrl, None  # ConstraintResults(leadPoint, leadOrient )
Ejemplo n.º 25
0
def buildDogleg(hipJoint,
                end,
                pvLen=None,
                name='Dogleg',
                endOrientType=util.EndOrient.TRUE_ZERO_FOOT,
                groupName='',
                controlSpec={}):
    '''
    ..  todo::
        * Specify toe joint instead to remove ambiguity in case of twist joints.
        * For some reason, sometimes, twist must be introduced because some flippin
            occurs.  For some reason the poleVector doesn't come in straight on.
            * Need to determine if a 180 twist is needed as the minotaur did.
        * Need to figure out the best way to constrain the last joint to the controller
    '''

    boundChain = util.getChain(hipJoint, end)

    container = group(n=name + '_dogHindleg', em=True, p=node.mainGroup())

    # &&& I think I want to turn this into the container for all extra stuff related to a given control
    chainGrp = group(p=container, n=name + "_ikChain", em=True)
    parentConstraint(hipJoint.getParent(), chainGrp, mo=True)

    # Make the control to translate/offset the limb's socket.
    socketOffset = controllerShape.build(
        name + '_socket',
        controlSpec['socket'],
        type=controllerShape.ControlType.TRANSLATE)
    pdil.dagObj.lock(socketOffset, 'r s')
    pdil.dagObj.moveTo(socketOffset, hipJoint)
    socketZero = pdil.dagObj.zero(socketOffset)
    socketZero.setParent(chainGrp)

    footCtrl = controllerShape.build(name,
                                     controlSpec['main'],
                                     type=controllerShape.ControlType.IK)
    pdil.dagObj.lock(footCtrl, 's')
    footCtrl.addAttr('bend', at='double', k=True)
    pdil.dagObj.moveTo(footCtrl, end)

    if endOrientType == util.EndOrient.TRUE_ZERO:
        util.trueZeroSetup(end, footCtrl)
    elif endOrientType == util.EndOrient.TRUE_ZERO_FOOT:
        util.trueZeroFloorPlane(end, footCtrl)
    elif endOrientType == util.EndOrient.JOINT:
        pdil.dagObj.matchTo(footCtrl, end)

        footCtrl.rx.set(util.shortestAxis(footCtrl.rx.get()))
        footCtrl.ry.set(util.shortestAxis(footCtrl.ry.get()))
        footCtrl.rz.set(util.shortestAxis(footCtrl.rz.get()))

        pdil.dagObj.zero(footCtrl)
    elif endOrientType == util.EndOrient.WORLD:
        # Do nothing, it's built world oriented
        pass

    util.createMatcher(footCtrl, end).setParent(container)

    # Make the main ik chain which gives overall compression
    masterChain = util.dupChain(hipJoint, end)
    masterChain[0].rename(pdil.simpleName(hipJoint, '{0}_OverallCompression'))

    mainIk = util.ikRP('mainIk', masterChain[0], masterChain[-1])
    PyNode('ikSpringSolver').message >> mainIk.ikSolver
    '''
    mainIk = ikHandle( sol='ikRPsolver', sj=masterChain[0], ee=masterChain[-1] )[0]
    PyNode('ikSpringSolver').message >> mainIk.ikSolver
    
    mainIk.rename('mainIk')
    hide(mainIk)
    '''

    springFixup = group(em=True, n='SprinkIkFix')
    springFixup.inheritsTransform.set(False)
    springFixup.inheritsTransform.lock()
    springFixup.setParent(socketOffset)
    pointConstraint(socketOffset, springFixup)
    masterChain[0].setParent(springFixup)

    #pointConstraint( socketOffset, hipJoint )

    # Create the polevector.  This needs to happen first so things don't flip out later
    out = util.calcOutVector(masterChain[0], masterChain[1], masterChain[-1])
    if not pvLen or pvLen < 0:
        pvLen = util.chainLength(masterChain[1:]) * 0.5
    pvPos = out * pvLen + dt.Vector(
        xform(boundChain[1], q=True, ws=True, t=True))

    pvCtrl = controllerShape.build(name + '_pv',
                                   controlSpec['pv'],
                                   type=controllerShape.ControlType.POLEVECTOR)
    pdil.dagObj.lock(pvCtrl, 'r s')
    xform(pvCtrl, ws=True, t=pvPos)
    poleVectorConstraint(pvCtrl, mainIk)

    # Verify the knees are in the same place
    delta = boundChain[1].getTranslation(
        'world') - masterChain[1].getTranslation('world')
    if delta.length() > 0.1:
        mainIk.twist.set(180)

    # Make sub IKs so the chain can be offset
    offsetChain = util.dupChain(hipJoint, end, '{0}_offset')
    hide(offsetChain[0])
    #offsetChain[0].rename( 'OffsetChain' )
    offsetChain[0].setParent(container)
    controllerShape.connectingLine(pvCtrl, offsetChain[1])
    constraints = util.constrainAtoB(util.getChain(hipJoint, end),
                                     offsetChain,
                                     mo=False)

    pointConstraint(masterChain[0], offsetChain[0])

    ankleIk = util.ikRP('hipToAnkle', offsetChain[0], offsetChain[-2])
    offsetIk = util.ikRP('metatarsusIk', offsetChain[-2], offsetChain[-1])

    #ankleIk = ikHandle( sol='ikRPsolver', sj=offsetChain[0], ee=offsetChain[-2])[0]
    #offsetIk = ikHandle( sol='ikRPsolver', sj=offsetChain[-2], ee=offsetChain[-1])[0]
    #offsetIk.rename('metatarsusIk')

    bend = controllerShape.build(name + '_bend',
                                 controlSpec['bend'],
                                 type=controllerShape.ControlType.ROTATE)
    pdil.dagObj.matchTo(bend, masterChain[-1])

    if end.tx.get() < 0:
        pdil.anim.orientJoint(bend,
                              boundChain[-2],
                              upTarget=boundChain[-3],
                              aim='-y',
                              up='-x')
    else:
        pdil.anim.orientJoint(bend,
                              boundChain[-2],
                              upTarget=boundChain[-3],
                              aim='y',
                              up='x')

    bendZero = pdil.dagObj.zero(bend)
    bendZero.setParent(footCtrl)
    pdil.dagObj.lock(bend, 't s')
    orientConstraint(masterChain[-1], bendZero, mo=True)

    ##    offsetControl = group(em=True, n='OffsetBend')
    ##    offsetContainer = group(offsetControl, n='OffsetSpace')
    ##    offsetContainer.setParent(footCtrl)

    # Setup the offsetContainer so it is properly aligned to bend on z
    ##    offsetContainer.setParent( masterChain[-1] )
    ##    offsetContainer.t.set(0, 0, 0)
    #temp = aimConstraint( pvCtrl, offsetContainer, aim=[1, 0, 0], wut='object', wuo=hipJoint, u=[0, 1, 0])
    #delete( temp )
    '''
    NEED TO CHANGE THE ORIENTATION
    
    Must perfectly align with ankle segment so the offset ikhandle can translate
    according to how much things are scaled
    
    '''
    ##    lib.anim.orientJoint(offsetContainer, boundChain[-2], upTarget=boundChain[-3], aim='y', up='x')
    #mimic old way lib.anim.orientJoint(offsetContainer, pvCtrl, upTarget=hipJoint, aim='x', up='y')
    #lib.anim.orientJoint(offsetContainer, pvCtrl, upTarget=hipJoint, aim='x', up='y')

    ##    offsetControl.t.set(0, 0, 0)
    ##    offsetControl.t.lock()
    ##    offsetControl.r.set(0, 0, 0)
    ##    footCtrl.bend >> offsetControl.rz
    '''
    This is really dumb.
    Sometimes maya will rotate everything by 180 but I'm not sure how to
    calculate the proper offset, which normally results in one axis being off
    by 360, so account for that too.
    '''
    temp = orientConstraint(footCtrl, offsetChain[-1], mo=True)

    if not pdil.math.isClose(offsetChain[-1].r.get(), [0, 0, 0]):

        badVals = offsetChain[-1].r.get()
        delete(temp)
        offsetChain[-1].r.set(-badVals)
        temp = orientConstraint(footCtrl, offsetChain[-1], mo=True)

        for a in 'xyz':
            val = offsetChain[-1].attr('r' + a).get()
            if abs(val - 360) < 0.00001:
                attr = temp.attr('offset' + a.upper())
                attr.set(attr.get() - 360)

            elif abs(val + 360) < 0.00001:
                attr = temp.attr('offset' + a.upper())
                attr.set(attr.get() + 360)
    # Hopefully the end of dumbness

    ankleIk.setParent(bend)

    # Adjust the offset ikHandle according to how long the final bone is.

    masterChain[-1].tx >> ankleIk.ty
    #    if masterChain[-1].tx.get() > 0:
    #        masterChain[-1].tx >> ankleIk.ty
    #    else:
    #        pdil.math.multiply(masterChain[-1].tx, -1.0) >> ankleIk.ty

    ankleIk.tx.lock()
    ankleIk.tz.lock()

    #ankleIk.t.lock()

    mainIk.setParent(footCtrl)
    offsetIk.setParent(footCtrl)

    pdil.dagObj.zero(footCtrl).setParent(container)

    hide(masterChain[0])
    poleVectorConstraint(pvCtrl, ankleIk)
    poleVectorConstraint(pvCtrl, offsetIk)

    # Adding the pv constraint might require a counter rotation of the offsetIk
    counterTwist = offsetChain[-2].rx.get() * (
        1.0 if offsetChain[-2].tx.get() < 0 else -1.0)
    offsetIk.twist.set(counterTwist)

    pdil.dagObj.zero(pvCtrl).setParent(container)

    # Make stretchy ik, but the secondary chain needs the stretch hooked up too.
    util.makeStretchyNonSpline(footCtrl, mainIk)

    for src, dest in zip(masterChain[1:], offsetChain[1:]):
        src.tx >> dest.tx

    footCtrl = pdil.nodeApi.RigController.convert(footCtrl)
    footCtrl.container = container
    footCtrl.subControl['socket'] = socketOffset
    footCtrl.subControl['pv'] = pvCtrl
    footCtrl.subControl['bend'] = bend

    # Add default spaces
    space.addMain(pvCtrl)
    space.add(pvCtrl, footCtrl)
    space.add(pvCtrl, footCtrl, mode=space.Mode.TRANSLATE)
    if hipJoint.getParent():
        space.add(pvCtrl, hipJoint.getParent())

        space.addMain(footCtrl)
        space.add(footCtrl, hipJoint.getParent())

    return footCtrl, constraints
Ejemplo n.º 26
0
def generateReposer(cards=None, placeholder=False, progress=None):
    ''' If no cards are specificed, a new reposer is build, otherwise it
    rebuilds/adds reposers for the specified cards.
    
    
    Args:
        cards
        placeholder
        progress: Optional `progressWindow` that will be `.update()`'d twice for
            each card, MUST be preconfigured (in case several things are updating)
    
    &&& TODO Verify the cards can be built in any order
    '''
    
    global jointMapping # global'd for debugging
    suffix = '_placeholder' if placeholder else ''
    
    rJoints = []
    rCards = []
    unlock = {} # <repose joint or card>: <list of attrs to be re-locked>

    jointMapping = {}  # Lazy "bi-directional mapping" of bpj <-> reposeJoint, both are added as keys to eachother

    # Build all the cards and joints
    if not cards:
        cards = find.blueprintCards()
        # Delete previous roots
        for oldRoot in getReposeRoots():
            oldRoot.deleteAttr('reposeRoot')
            
    # Otherwise populate the containers with the existing reposer to build/add new stuff.
    else:
        #allExistingRCards = set( cmds.ls( '*.bpCard', o=True, r=True, l=True ) )
        allExistingRJoints = set( cmds.ls( '*.bpj', o=True, r=True, l=True ) )
        
        for oldRoot in getReposeRoots():
            joints = cmds.listRelatives( str(oldRoot), f=True, ad=True, type='joint' )
            joints = [PyNode(c) for c in allExistingRJoints.intersection(joints)]
        
            for rj in joints:
                bpj = rj.bpj.listConnections()[0]
                jointMapping[rj] = bpj
                jointMapping[bpj] = rj

    
    for card in cards:
        if progress:
            progress.update()
        
        rCard = duplicate(card, po=0)[0]
        showHidden(rCard)
        pdil.dagObj.unlock(rCard)
        stripReposerCard(rCard)
        
        targetName = simpleName(card, '{}_repose' + suffix)
        
        previous, attrs = reposeLink(card, rCard, 'bpCard') if not placeholder else (None, [])
        unlock[rCard] = attrs
        
        renameReposeObj(rCard, targetName, previous)
        
        for child in rCard.listRelatives():
            if not child.type() == 'nurbsSurface':
                delete(child)
        rCards.append(rCard)
        makeIdentity(rCard, t=False, r=False, s=True, apply=True)
        pdil.dagObj.lock( rCard, 's' )

        for jnt in card.joints:
            reposeJoint = joint(None)
            targetName = simpleName(jnt, '{}_repose' + suffix)
            
            previous, attrs = reposeLink(jnt, reposeJoint, 'bpj') if not placeholder else (None, [])
            unlock[reposeJoint] = attrs
            renameReposeObj(reposeJoint, targetName, previous)

            pdil.dagObj.matchTo(reposeJoint, jnt)

            #assert jnt.info.get('options', {}).get('mirroredSide', False) is False, 'parent to mirrored joints not supported yet'
            
            jointMapping[jnt] = reposeJoint
            jointMapping[reposeJoint] = jnt

            rJoints.append(reposeJoint)
            
    
    # Set their parents
    for reposeJoint in rJoints:
        parent = jointMapping[reposeJoint].parent
        if parent in jointMapping: # Check against joint mapping in case only a few selected cards a being tposed
            reposeJoint.setParent( jointMapping[parent] )
    
    reposeContainer = getReposeContainer()
    
    
    # Put under cards, card pivot to lead joint
    for rCard, card in zip(rCards, cards):
        if progress:
            progress.update()
        bpj = card.parentCardJoint
        #print('BPJ - - - - ', bpj, bpj in jointMapping)
        if bpj in jointMapping:
            start = card.start() if card.joints else bpj
            #rCard.setParent( getRJoint(bpj) )
            pdil.dagObj.unlock(rCard)
            
            #firstBpj = card.joints[0]
            #return
            
            isMirrored = card.isCardMirrored()
            
            
            mirroredSide = card.joints[0].info.get('options', {}).get('mirroredSide')
            #print('rCard.mirror', rCard.mirror, 'info:', mirroredSide)
            #if rCard.mirror is False and mirroredSide:
            if isMirrored is False and card.mirror is False and mirroredSide:
                #print('opposite mirror')
                rCard.setParent( makeMirrored( jointMapping[bpj] ) )
            else:
                #print('regular side stuff')
                rCard.setParent( jointMapping[bpj] )
                
            #cmds.parent(str(rCard), str(jointMapping[bpj]))
            xform(rCard, ws=True, piv=xform(start, q=True, t=True, ws=True) )
            pdil.dagObj.lock(rCard, 't')
            
        else:
            if not placeholder:
                rCard.addAttr('reposeRoot', at='message')
                rCard.setParent( reposeContainer )
                
        addVector(rCard, 'origRot', rCard.r.get())
        addVector(rCard, 'origTrans', rCard.t.get())
        #start = getRJoint(card.start())
        start = jointMapping[card.start()]
        start.setParent( rCard )
        pdil.dagObj.lock( start, 't s' )

        if rCard in unlock:
            for attr in unlock[rCard]:
                rCard.attr(attr).unlock()
                rCard.attr(attr).showInChannelBox(True)
                
    for reposeJoint in rJoints:
        pdil.dagObj.lock(reposeJoint, 'ry rz')
        pdil.dagObj.lock(reposeJoint, 't s') # I can't see why I wasn't locking t/s already.  Possible exception, `freeform`
        
        if reposeJoint in unlock:
            for attr in unlock[reposeJoint]:
                reposeJoint.attr(attr).unlock()
                reposeJoint.attr(attr).showInChannelBox(True)
        
        addVector(reposeJoint, 'origRot', reposeJoint.r.get())
        addVector(reposeJoint, 'origTrans', reposeJoint.t.get())
    
        '''
Ejemplo n.º 27
0
def twistSetup(control,
               twistJoints,
               startSegment,
               endSegment,
               jointLenMultiplier,
               twistLateralAxis=[0, 1, 0],
               driverLateralAxis=[0, 1, 0],
               defaultPower=0.5):
    '''
    Given a list of twist joints, an anchoring startSegment and the endSegment
    
    :param twistJoints: The joints that will be twisted
    :param twistDriver: The end joint that will influence the twisting
    
    
    TwistJoints bone's aim axis = the lateral axis
    TwistJoints Up axis = points to the target (wrist)

    Assumption, all the twist joints and start segment are oriented the same
        
    World up = object rotation
    up obj = target (wrist)
    up axis = I think this is the target's lateral axis

    '''

    #anchor = duplicate( twistJoints, po=True )[0]
    #anchor.rename( simpleName(jnt, '{0}Anchor') )

    for jnt in twistJoints:
        aimer = duplicate(jnt, po=True)[0]
        space = duplicate(jnt, po=True)[0]

        saveRestLength(space)
        pdil.math.multiply(space.restLength, jointLenMultiplier) >> space.tx

        aimer.rename(simpleName(jnt, '{0}Aimer'))
        space.rename(simpleName(jnt, '{0}Space'))
        space.drawStyle.set(2)

        jnt.setParent(space)

        #hide(anchor, aimer)
        hide(aimer)

        constraint = orientConstraint(startSegment, aimer, space)

        constraint.interpType.set(
            2)  # Set to "shortest" because it will flip otherwise.

        aimConstraint(
            endSegment,
            aimer,
            wut='objectrotation',
            wuo=endSegment,
            mo=True,
            u=twistLateralAxis,  # identifyAxis(jnt, asVector=True),  # noqa e127
            aimVector=[1, 0, 0],  # identifyAxis(jnt, asVector=True),
            wu=driverLateralAxis,
        )

        baseRotAttr, endRotAttr = constraint.getWeightAliasList()

        driver = drive(control,
                       simpleName(jnt, '{0}_Auto'),
                       endRotAttr,
                       minVal=0,
                       maxVal=1,
                       dv=defaultPower)
        pdil.math.opposite(driver) >> baseRotAttr
Ejemplo n.º 28
0
    def build(cls, targets, spaceName, spaceContainer, extraInfo, control,
              space):
        if not spaceName:
            spaceName = 'freeform'

        trueTarget = group(em=True,
                           name=pdil.simpleName(control, '{0}_freeform'),
                           p=spaceContainer)
        pdil.dagObj.matchTo(
            trueTarget, control
        )  # It's actually important so this can follow correctly. (line copied from MULTI_ORIENT)

        # Put proxies in a group that follows the "local" space.  Honestly this math doesn't make
        # sense but it gives the results JH wants, which are sensible.
        proxyGrp = group(em=True,
                         name=pdil.simpleName(control, '{0}_freeformProxies'),
                         p=spaceContainer)
        pdil.dagObj.matchTo(proxyGrp, control)
        parentConstraint(cls.getProxy(control), proxyGrp, mo=True)

        rProxies = []
        tProxies = []
        for t, (mode, w) in zip(targets, extraInfo):
            d = duplicate(trueTarget)[0]
            d.rename(pdil.simpleName(t) + '_freeform')
            d.setParent(proxyGrp)

            if mode == cls.ORIENT:
                orientConstraint(t, d, mo=True)
                rProxies.append((d, w))
            elif mode == cls.POINT:
                pointConstraint(t, d, mo=True)
                tProxies.append((d, w))
            elif mode == cls.PARENT:
                parentConstraint(t, d, mo=True)
                rProxies.append((d, w))
                tProxies.append((d, w))
            elif mode == cls.POINT_ORIENT:
                orientConstraint(t, d, mo=True)
                pointConstraint(t, d, mo=True)
                rProxies.append((d, w))
                tProxies.append((d, w))
            elif mode == cls.PARENT_TRANS:
                parentConstraint(t, d, mo=True, sr=['x', 'y', 'z'])
                tProxies.append((d, w))

        if rProxies:
            rConstraint = parentConstraint([p[0] for p in rProxies],
                                           trueTarget,
                                           mo=True,
                                           st=['x', 'y', 'z'])
            rConstraint.addAttr('rotTarget', at='bool', dv=True)
            rConstraint.interpType.set(2)

            attrs = rConstraint.getWeightAliasList()
            for v, attr in zip([p[1] for p in rProxies], attrs):
                attr.set(v)
        else:
            # If there were no rotate targets, use the parent joint or group
            proxy = cls.getProxy(control)
            if proxy:
                const = parentConstraint(proxy,
                                         trueTarget,
                                         mo=True,
                                         st=['x', 'y', 'z'])
                const.addAttr('mo_ignore', at='bool', dv=True)

        if tProxies:
            tConstraint = parentConstraint([p[0] for p in tProxies],
                                           trueTarget,
                                           mo=True,
                                           sr=['x', 'y', 'z'])

            attrs = tConstraint.getWeightAliasList()
            for v, attr in zip([p[1] for p in tProxies], attrs):
                attr.set(v)
        else:
            # If there were no translate targets, use the parent joint or group
            proxy = cls.getProxy(control)
            if proxy:
                const = parentConstraint(proxy,
                                         trueTarget,
                                         mo=True,
                                         sr=['x', 'y', 'z'])
                const.addAttr('mo_ignore', at='bool', dv=True)

        return trueTarget, spaceName
Ejemplo n.º 29
0
def add(control,
        target,
        spaceName='',
        modeName=common.Mode.ROTATE_TRANSLATE,
        enum=True,
        rotateTarget=None):
    ''' Add a space to the given control.
    
    Args:
        control: Add the space to it
        target: What to target
        modeName: String name for what type of space
        enum: DEPRECATED unused
        rotateTarget: Now a catch-all for lots of additional data depending on the mode
    '''

    # Early outs
    if not target:
        print("No target specified")
        return False

    for targetInfo in getTargetInfoBD(control):
        if targetInfo.type == modeName and targetInfo.target == target:
            print("Target already exists", modeName, target)
            return False

    if spaceName in common.getNames(control):
        return False
    # End early outs

    rotateLocked = False
    translateLocked = False
    # If the control can't translate, make sure the mode is rotate-only.
    if control.tx.isLocked() and control.ty.isLocked() and control.tz.isLocked(
    ):
        if modeName != 'MULTI_ORIENT':
            modeName = 'ROTATE'
        translateLocked = True

    if control.rx.isLocked() and control.ry.isLocked() and control.rz.isLocked(
    ):
        rotateLocked = True

    with pdil.dagObj.TemporaryUnlock(control,
                                     trans=not translateLocked,
                                     rot=not rotateLocked):
        space = pdil.dagObj.zero(control, apply=False)

        # &&& Hack to not make two user groups, unsure which direction is the best.
        # The class probably should make the object but I can't remember if that causes some side effect.
        spaceContainer = common.getGroup(
            modeName, main=find.mainGroup(
                fromControl=control)) if modeName != 'USER' else None

        # -----------------------
        # ACTUAL SPACE ADDED HERE
        # Call the appropriate sub function to build the particulars of the space
        trueTarget, spaceName = common.Mode.build(modeName, target, spaceName,
                                                  spaceContainer, rotateTarget,
                                                  control, space)
        # -----------------------

        if not spaceName:
            spaceName = pdil.simpleName(target)
        existingNames = common.getNames(control) + [spaceName]
        if not control.hasAttr(common.ENUM_ATTR):
            control.addAttr(common.ENUM_ATTR,
                            at='enum',
                            enumName='FAKE',
                            k=True)
        common.setNames(control, existingNames)

        choice = getChoiceNode(control)
        if not choice:
            choice = makeChoiceNode()
            decomp = createNode('decomposeMatrix')
            choice.output >> decomp.inputMatrix
            control.space >> choice.selector
        else:
            decomp = choice.output.listConnections()[0]

        offsetMatrix = addTarget(choice, trueTarget, control, space,
                                 choice.input.numElements())
        offsetMatrix.addAttr(common.SPACE_TYPE_NAME, dt='string')
        offsetMatrix.attr(common.SPACE_TYPE_NAME).set(modeName)

        if len(
                existingNames
        ) == 1:  # At this stage, this means this is the first space so decomp needs hookup.
            decomp.outputTranslate >> space.t
            decomp.outputRotate >> space.r

    return True
Ejemplo n.º 30
0
def buildSplineTwist(start, end, controlCountOrCrv=4, twistInfDist=0, simplifyCurve=True,
    tipBend=True, sourceBend=True, matchOrient=True, allowOffset=True,  # noqa e128
    useLeadOrient=False,  # This is an backwards compatible option, mutually exclusive with matchOrient
    twistStyle=TwistStyle.ADVANCED, duplicateCurve=True,
    controlOrient=OrientMode.CLOSEST_JOINT,
    name='', groupName='', controlSpec={}):
    '''
    Make a spline controller from `start` to `end`.
    
    :param int twistInfDist: Default twist controls to falloff before hitting eachother.
        Otherwise it is the number of joints on either side it will influence.
    :param bool simplifyCurve:  Only used if # of cvs is specified.  Turning it
        on will likely result it the curve not matching the existing joint position
        but will be more evenly spaced per control.
    :param bool tipBend:  If True, an extra cv will be added at the second to
        last joint, controlled by the last controller to ease out.
        
    ##:param bool applyDirectly: If True, rig the given joints, do not make a duplicate chain
        
    :param bool useLeadOrient: If True, the controllers will be aligned the same
        as the first joint.
        **NOTE** I think this option only exists to preserve previous builds, this is pretty dumb
        
    :param bool matchOrient: Does trueZero on the start and end.  I'm not sure this makes sense.
        
    
    
    ..  todo::
        * Add the same spline chain +X towards child that the neck has and test out advancedTwist()
    
        * See if I can identify the closest joint to a control and orient to that
        * The first joint has parent AND local, which are the same thing, keep this for convenience of selecting all the controls and editing attrs?
        * Test specifying your own curve
        * There is a float division error that can happen if there are too many control cvs.
        * Verify twists work right with unsimplified curves (hint, I don't think they do).
    '''
    
    matchOrient = False
    useLeadOrient = False
    
    if isinstance( controlCountOrCrv, int ):
        assert controlCountOrCrv > 3, "controlCount must be at least 4"
    
    # The axis to twist and stretch on.
    jointAxis = util.identifyAxis( start.listRelatives(type='joint')[0] )
    
    # Make a duplicate chain for the IK that will also stretch.
    stretchingChain = util.dupChain( start, end, '{0}_stretch' )
    
    # &&& NOTE!  This might affect advanced twist in some way.
    # If the chain is mirrored, we need to reorient to point down x so the
    # spline doesn't mess up when the main control rotates
    if stretchingChain[1].tx.get() < 0:
        # Despite aggresive zeroing of the source, the dup can still end up slightly
        # off zero so force it.
        for jnt in stretchingChain:
            jnt.r.set(0, 0, 0)
        joint( stretchingChain[0], e=True, oj='xyz', secondaryAxisOrient='yup', zso=True, ch=True)
        joint( stretchingChain[-1], e=True, oj='none')
    
    if isinstance( controlCountOrCrv, int ):
        mainIk, _effector, crv = ikHandle( sol='ikSplineSolver',
            sj=stretchingChain[0],
            ee=stretchingChain[-1],
            ns=controlCountOrCrv - 3,
            simplifyCurve=simplifyCurve)
    else:
        if duplicateCurve:
            crv = duplicate(controlCountOrCrv)[0]
        else:
            crv = controlCountOrCrv
            
        mainIk, _effector = ikHandle( sol='ikSplineSolver',
            sj=stretchingChain[0],
            ee=stretchingChain[-1],
            ccv=False,
            pcv=False)
        crv.getShape().worldSpace[0] >> mainIk.inCurve
    
    hide(mainIk)
    mainIk.rename( pdil.simpleName(start, "{0}_ikHandle") )
    crv.rename( pdil.simpleName(start, "{0}_curve") )
        
    if not name:
        name = util.trimName(start)

    if name.count(' '):
        name, endName = name.split()
    else:
        endName = ''
    
    # Only add a tipBend cv if number of cvs was specified.
    if tipBend and isinstance( controlCountOrCrv, int ):
        currentTrans = [ xform(cv, q=True, ws=True, t=True) for cv in crv.cv ]
        insertKnotCurve( crv.u[1], nk=1, add=False, ib=False, rpo=True, cos=True, ch=True)
        for pos, cv in zip(currentTrans, crv.cv[:-2]):
            xform( cv, ws=True, t=pos )
    
        xform( crv.cv[-2], ws=True, t=xform(end.getParent(), q=True, ws=True, t=True) )
        xform( crv.cv[-1], ws=True, t=currentTrans[-1] )
        
    # Only add a sourceBend cv if number of cvs was specified.
    if sourceBend and isinstance( controlCountOrCrv, int ):
        currentTrans = [ xform(cv, q=True, ws=True, t=True) for cv in crv.cv ]
        insertKnotCurve( crv.u[1.2], nk=1, add=False, ib=False, rpo=True, cos=True, ch=True)  # I honestly don't know why, but 1.2 must be different than 1.0
        for pos, cv in zip(currentTrans[1:], crv.cv[2:]):
            xform( cv, ws=True, t=pos )
    
        xform( crv.cv[0], ws=True, t=currentTrans[0] )
        xform( crv.cv[1], ws=True, t=xform(stretchingChain[1], q=True, ws=True, t=True) )
    
    grp = group(em=True, p=node.mainGroup(), n=start.name() + "_splineTwist")
    
    controls = util.addControlsToCurve(name + 'Ctrl', crv, controlSpec['main'])
    for ctrl in controls:
        pdil.dagObj.zero(ctrl).setParent( grp )


    if controlOrient == OrientMode.CLOSEST_JOINT:
        # Use the real chain to match orientations since the stretching chain might reorient to compensate for mirroring.
        jointPos = {j: dt.Vector(xform(j, q=True, ws=True, t=True)) for j in util.getChain(start, end)}
        
        aveSpacing = util.chainLength(stretchingChain) / (len(stretchingChain) - 1)
        
        for ctrl in controls:
            cpos = dt.Vector(xform(ctrl, q=True, ws=True, t=True))
            distances = [ ( (jpos - cpos).length() / aveSpacing, j) for j, jpos in jointPos.items()  ]
            distances.sort()
            
            
            ''' Just use the closest joint if within 10% of the average spacing
            Possible future improvement, look at two joints, and determine if
            the control is between them and inbetween the orientation.
            '''
            if True:  # distances[0][0] < 100:
                r = xform(distances[0][1], q=True, ro=True, ws=True)

                with pdil.dagObj.Solo(ctrl):
                    xform(ctrl, ro=r, ws=True)
                    pdil.dagObj.zero(ctrl)

    if endName:
        controls[-1].rename(endName + 'Ctrl')

    if matchOrient:
        util.trueZeroSetup(start, controls[0])
        util.trueZeroSetup(end, controls[-1])

    if tipBend:
        if useLeadOrient and not matchOrient:
            controls[-1].setRotation( end.getRotation(space='world'), space='world' )
        
        parent( controls[-2].getChildren(), controls[-1] )
        name = controls[-2].name()
        delete( pdil.dagObj.zero(controls[-2]) )

        if not endName:
            controls[-1].rename(name)
        controls[-2] = controls[-1]
        controls.pop()
        #core.dagObj.zero(controls[-2]).setParent(controls[-1])
        #channels = [t + a for t in 'trs' for a in 'xyz']
        #for channel in channels:
        #    controls[-2].attr( channel ).setKeyable(False)
        #    controls[-2].attr( channel ).lock()
           
    if sourceBend:
        names = []
        
        for ctrl in controls[1:-1]:
            names.append( ctrl.name() )
            ctrl.rename( '__temp' )
        
        endNum = -1 if endName else None
        for name, cur in zip(names, controls[2:endNum] ):
            cur.rename(name)
            
        if useLeadOrient and not matchOrient:
            controls[0].setRotation( start.getRotation(space='world'), space='world' )
            
        parent( controls[1].getChildren(), controls[0] )
        delete( pdil.dagObj.zero(controls[1]) )
        
        del controls[1]
        
    controls[0] = pdil.nodeApi.RigController.convert(controls[0])
    controls[0].container = grp
    
    stretchAttr, jointLenMultiplier = util.makeStretchySpline(controls[0], mainIk)
        
    connectingCurve = addConnectingCurve(controls)
    controls[0].visibility >> connectingCurve.visibility
    
    # Make twist for everything but hide them all and drive the ones that overlap
    # with spline controllers by the spline control.
    if not twistInfDist:
        numJoints = countJoints(start, end)
        twistInfDist = int(math.ceil( numJoints - len(controls) ) / float(len(controls) - 1))
        twistInfDist = max(1, twistInfDist)
    
    noInherit = group(em=True, p=grp, n='NoInheritTransform')
    pdil.dagObj.lock(noInherit)
    noInherit.inheritsTransform.set(False)
    noInherit.inheritsTransform.lock()

    # &&& If simplify curve is ON, the last joint gets constrained to the spinner?
    # Otherwise it gets constrained to the offset or stretch joint, which I think is correct.
    
    if allowOffset:
        # If allowOffset, make another chain to handle the difference in joint positions.
        offsetChain = util.dupChain( start, end, '{0}_offset' )

        offsetChain[0].setParent(noInherit)
        hide(offsetChain[0])
        twists, constraints = addTwistControls( offsetChain, start, end, twistInfDist)
        finalRigJoint = offsetChain[-1]
    else:
        twists, constraints = addTwistControls( stretchingChain, start, end, twistInfDist )
        finalRigJoint = stretchingChain[-1]
    
    # Constrain the end to the last controller so it doesn't pop off at all,
    # but still respect the stretch attr.
    pointConstraint(finalRigJoint, end, e=True, rm=True)
    
    # Make a proxy that can allows respecting stretch being active or not.
    endProxy = duplicate(end, po=True)[0]
    endProxy.rename('endProxy')
    hide(endProxy)
    endProxy.setParent(grp)
    
    stretchAttr >> pdil.constraints.pointConst( controls[-1], endProxy, mo=True )
    pdil.math.opposite(stretchAttr) >> pdil.constraints.pointConst( finalRigJoint, endProxy )
    constraints.point >> pdil.constraints.pointConst( endProxy, end )
    
    hide(twists)
    
    numControls = len(controls)
    numTwists = len(twists)
    for i, ctrl in enumerate(controls):
        index = int(round( i * ((numTwists - 1) / (numControls - 1)) ))
        util.drive( ctrl, 'twist', twists[index].attr('r' + jointAxis) )
        space.add( ctrl, start.getParent(), 'local' )
    
    parents = [start.getParent()] + controls[:-1]
    
    stretchingChain[0].setParent(noInherit)
    crv.setParent(noInherit)
    hide(crv, stretchingChain[0])
    connectingCurve.setParent( noInherit )
    
    mainIk.setParent(grp)
    
    # Do not want to scale but let rotate for "fk-like" space mode
    for ctrl, _parent in zip(controls, parents):
        pdil.dagObj.lock( ctrl, 's' )
        
        if useLeadOrient:
            ctrl.setRotation( start.getRotation(space='world'), space='world' )
            pdil.dagObj.zero(ctrl)
        
        space.addMain(ctrl)
        space.add( ctrl, _parent, 'parent')
    
    for i, ctrl in enumerate(controls[1:]):
        controls[0].subControl[str(i)] = ctrl
    
    # Must constrain AFTER controls (possibly) get orientd
    orientConstraint( controls[-1], finalRigJoint, mo=True )

    # Setup advanced twist
    if twistStyle == TwistStyle.ADVANCED:
        # &&& Test using advancedTwist() to replace the code beloew
        util.advancedTwist(stretchingChain[0], stretchingChain[1], controls[0], controls[-1], mainIk)
        '''
        startAxis = duplicate( start, po=True )[0]
        startAxis.rename( 'startAxis' )
        startAxis.setParent( controls[0] )
        
        endAxis = duplicate( start, po=True )[0]
        endAxis.rename( 'endAxis' )
        endAxis.setParent( controls[-1] )
        endAxis.t.set(0, 0, 0)
        
        mainIk.dTwistControlEnable.set(1)
        mainIk.dWorldUpType.set(4)
        startAxis.worldMatrix[0] >> mainIk.dWorldUpMatrix
        endAxis.worldMatrix[0] >> mainIk.dWorldUpMatrixEnd
        
        hide(startAxis, endAxis)
        '''
    else:
        if twistStyle == TwistStyle.X:
            controls[-1].rx >> mainIk.twist
        elif twistStyle == TwistStyle.NEG_X:
            pdil.math.multiply(controls[-1].rx, -1.0) >> mainIk.twist
            
        elif twistStyle == TwistStyle.Y:
            controls[-1].ry >> mainIk.twist
        elif twistStyle == TwistStyle.NEG_Y:
            pdil.math.multiply(controls[-1].ry, -1.0) >> mainIk.twist
            
        elif twistStyle == TwistStyle.Z:
            controls[-1].rz >> mainIk.twist
        elif twistStyle == TwistStyle.NEG_Z:
            pdil.math.multiply(controls[-1].rz, -1.0) >> mainIk.twist
        
        # To make .twist work, the chain needs to follow parent joint
        follow = group(em=True, p=grp)
        target = start.getParent()
        pdil.dagObj.matchTo(follow, stretchingChain[0])
        parentConstraint( target, follow, mo=1 )
        follow.rename(target + '_follow')
        stretchingChain[0].setParent(follow)
        
    # Constraint the offset (if exists) to the stretch last to account for any adjustments.
    if allowOffset:
        util.constrainAtoB(offsetChain[:-1], stretchingChain[:-1])
        pointConstraint(stretchingChain[-1], offsetChain[-1], mo=True)

    return controls[0], constraints