def allCards(main=None): ''' Return all the cards, optionally taking a specific skeletonBlueprint. ''' # Use cmds for speed (swift takes 0.20 with pymel but 0.04 with cmds) targetCards = set( cmds.ls( '*.skeletonInfo', o=True, r=True, l=True ) + cmds.ls( '*.fossilRigData', o=True, r=True, l=True ) ) if main: targetCards.intersection_update( cmds.listRelatives(main.name(), ad=True, type='transform', f=True) ) def order(obj): try: return cmds.getAttr(obj + '.buildOrder') except Exception: pass try: return json.loads(cmds.getAttr(obj + '.fossilRigData')).get('buildOrder', 10) except Exception: pass return 10 return [PyNode(c) for c in sorted(targetCards, key=order)]
def readSpec(self, spec): if 'id' in spec: cards = cmds.ls( '*.fossilRigData', o=True, r=True, l=True ) for card in cards: data = json.loads( cmds.getAttr( card + '.fossilRigData' ) ) if spec['id'] == data.get('id', None): return PyNode(card) return None
def controllers(main=None): ''' Returns all the animation controllers in the scene. .. todo:: Add the shapes that have ik/fk switching on them ''' allControls = set( cmds.ls('*.' + config.FOSSIL_CTRL_TYPE, o=True, r=True, l=True)) if main: mainTransforms = cmds.listRelatives(main.name(), ad=True, type='transform', f=True) if mainTransforms: allControls.intersection_update(mainTransforms) controls = [PyNode(o) for o in allControls] controls.append(main) root = rootMotion(main=main) if root: controls.append(root) return controls else: warning("No sub controls found for {0}".format(main.name())) else: controls = [PyNode(o) for o in allControls] main = mainGroup() if main: controls.append(main) root = rootMotion(main=main) if root: controls.append(root) return controls return []
def getSwitcherPlug(obj): ''' Will return either a string like "Arm_L_FKIK_SWITCH" or empty string if no switcher is found. Can't use pymel to avoid warnings of invalid node (nurbs with no cvs). This also means listRelatives returns None instead of []. Lame. ''' shapes = cmds.listRelatives(str(obj), type='nurbsCurve', f=True) if shapes: for shape in shapes: # Check for new style switch first if cmds.objExists(shape + '.IkSwitch'): return shape + '.IkSwitch' # Fallback to old style switch if cmds.objExists(shape + '.kinematicSwitch'): attr = cmds.listAttr(shape, ud=1, st='*_Switch') return cmds.ls(shape, l=False)[0] + '.' + attr[0] # noqa return ''
def readCardPath(cpath): res = ast.parse(cpath) body = res.body[0] #if isinstance(body, ast.Subscript): if isinstance(body.value, ast.Subscript): assert body.value.value.attr == 'subControl' cardCallRes = body.value.value.value.value subName = body.value.slice.value.s side = cardCallRes.attr motionType = body.value.value.value.attr cardCall = cardCallRes cardName = cardCall.value.args[0].s.rsplit('|', 1)[-1] else: cardCallRes = body.value subName = None motionType = cardCallRes.attr side = cardCallRes.value.attr cardCall = cardCallRes.value cardName = cardCall.value.args[0].s.rsplit('|', 1)[-1] if cardCall.value.keywords: assert cardCall.value.keywords[0].arg == 'cardId' cardId = cardCall.value.keywords[0].value else: cardId = None targetCard = None cards = cmds.ls('*.fossilRigData', o=True, r=True, l=True) if cardId: for card in cards: data = json.loads(cmds.getAttr(card + '.fossilRigData')) if cardId == data.get('id', None): targetCard = PyNode(card) break if not targetCard: names = {card.rsplit('|', 1)[-1]: card for card in cards} if cardName in names: targetCard = PyNode(names[cardName]) else: shortNames = { card.rsplit('|', 1)[-1].rsplit(':', 1)[-1]: card for card in cards } cardShortName = cardName.rsplit(':', 1)[-1] if cardShortName in shortNames: targetCard = PyNode(shortNames[cardShortName]) if not targetCard: return None #mainControl = targetCard.attr(motionType) #print(targetCard, motionType, side) mainControl = getattr(getattr(targetCard, side), motionType) if subName: return mainControl.subControl[subName] else: return mainControl # findSDK and applySDK updated to idSpec
def buildBones(cards=None, removeTempBind=True): global _meshStorage if not cards: cards = set(util.selectedCards()) if not cards: pdil.ui.notify(m='No cards selected') return issues = validateBoneNames(cards) if issues: pdil.ui.notify(m='\n'.join(issues), t='Fix these') return cardBuildOrder = find.cardJointBuildOrder() skinning.cacheWeights(cards, _meshStorage) useRepose = tpose.reposerExists() if useRepose: log.debug('Reposer Exists') realJoints = [ ] # This is a list of the joints that are ACTUALLY built in this process bindPoseJoints = [] # Matches realJoints, the cards that are bound tempBindJoints = [ ] # Super set of bindPoseJoints, including the hierarchy leading up to the bindBoseJoints estRealJointCount = len(cmds.ls('*.realJoint')) with pdil.ui.progressWin(title='Build Bones', max=estRealJointCount * 3 + len(cards)) as prog: # Build the tpose joints with tpose.matchReposer(cardBuildOrder): for card in cardBuildOrder: if card in cards: newJoints = card.buildJoints_core( nodeApi.fossilNodes.JointMode.tpose) realJoints += newJoints accessoryFixup(newJoints, card) prog.update() # The hierarchy has to be built to determine the right bindZero, so build everything if all cards # are being made, otherwise target just a few if len(cardBuildOrder) == len(cards): bindCardsToBuild = cardBuildOrder else: bindCardsToBuild = getRequiredHierarchy(cards) # Temp build the bind pose joints for card in bindCardsToBuild: joints = card.buildJoints_core( nodeApi.fossilNodes.JointMode.bind) tempBindJoints += joints if card in cards: bindPoseJoints += joints with tpose.goToBindPose(): # Setup all the constraints first so joint order doesn't matter constraints = [] prevTrans = [] for bind, real in zip(bindPoseJoints, realJoints): #with core.dagObj.Solo(bind): # bind.jo.set( real.jo.get() ) prevTrans.append(real.t.get()) constraints.append([ orientConstraint(bind, real), pointConstraint(bind, real) ]) real.addAttr('bindZero', at='double3') real.addAttr('bindZeroX', at='double', p='bindZero') real.addAttr('bindZeroY', at='double', p='bindZero') real.addAttr('bindZeroZ', at='double', p='bindZero') prog.update() #real.addAttr( 'bindZeroTr', at='double3' ) #real.addAttr( 'bindZeroTrX', at='double', p='bindZeroTr' ) #real.addAttr( 'bindZeroTrY', at='double', p='bindZeroTr' ) #real.addAttr( 'bindZeroTrZ', at='double', p='bindZeroTr' ) #real.bindZero.set( real.r.get() ) # Harvest all the values for real in realJoints: real.bindZero.set(real.r.get()) #real.bindZeroTr.set( real.t.get() ) prog.update() # Return the real joints back to their proper location/orientation for constraint, real, trans in zip(constraints, realJoints, prevTrans): delete(constraint) real.r.set(0, 0, 0) real.t.set(trans) prog.update() root = node.getTrueRoot() topJoints = root.listRelatives(type='joint') for jnt in topJoints: try: index = realJoints.index(jnt) real = jnt bind = bindPoseJoints[index] real.addAttr('bindZeroTr', at='double3') real.addAttr('bindZeroTrX', at='double', p='bindZeroTr') real.addAttr('bindZeroTrY', at='double', p='bindZeroTr') real.addAttr('bindZeroTrZ', at='double', p='bindZeroTr') delta = bind.worldMatrix[0].get( ) * real.worldInverseMatrix[0].get() real.bindZeroTr.set(delta[3][:3]) except ValueError: pass if removeTempBind: delete(tempBindJoints) else: log.debug('No reposer') # Only build the selected cards, but always do it in the right order. with pdil.ui.progressWin(title='Build Bones', max=len(cards)) as prog: for card in cardBuildOrder: if card in cards: newJoints = card.buildJoints_core( nodeApi.fossilNodes.JointMode.default) accessoryFixup(newJoints, card) prog.update() if useRepose: with tpose.goToBindPose(): skinning.loadCachedWeights(_meshStorage) else: skinning.loadCachedWeights(_meshStorage) select(cards)
def buildRig(cards=None): ''' Makes the rig, saving shapes and removing the old rig if needed. ''' # &&& Need to move find.cardJointBuildOrder to util and make selectedCards() use it. if not cards: cards = set(util.selectedCards()) mode = 'Use Rig Info Shapes' if not cards: pdil.ui.notify(m='No cards to selected to operate on.') return # &&& BUG, need to ONLY check against joints under the root, since other non-joint objects might have the same name # allJoints = ... for card in cards: joints = card.getOutputJoints() if joints and card.rigData.get( 'rigCmd', '') != 'Group': # Group doesn't build joints if len(cmds.ls(joints)) != len( joints ): # &&& This is a bad check, a reference mesh joints can mess it up. # &&& Ideall this prompts to build joints pdil.ui.notify(m='{} does not have joints built.'.format(card)) raise Exception('Joints not built') a = pdil.debug.Timer('Overall build') cardBuildOrder = find.cardJointBuildOrder() with tpose.matchReposer( cardBuildOrder) if tpose.reposerExists() else nothing(): with pdil.ui.progressWin(title='Building', max=len(cards) * 3) as pr: for card in cardBuildOrder: if card not in cards: continue pr.update(status=card.name() + ' prep') if mode == 'Use Current Shapes': card.saveShapes() # If this being rebuilt, also restore the if it's in ik or fk switchers = [ controllerShape.getSwitcherPlug(x[0]) for x in card._outputs() ] prevValues = [(s, getAttr(s)) for s in switchers if s] card.removeRig() pr.update(status=card.name() + ' build') _buildRig([card]) pr.update(status=card.name() + ' build') if mode != 'Use Rig Info Shapes': card.restoreShapes() # Restore ik/fk-ness for switch, value in prevValues: if objExists(switch): setAttr(switch, value) tpose.markBindPose(cards) select(cards) a.stop()
def load(filename, insertTime=None, alterPlug=None, bufferKeys=True, targetPool=None): ''' Loads a file containing animCurves (made with `save`) and hooks them up. :param func alterPlug: If the input needs some sort of transformation, provide a function that takes the plug string, ex "someSphere.tx" and returns a plug string of how it maps back, ex "zCube.tx" or "zCube.ty" and a function to alter the curve (or None) def alterPlug( 'inputNode.attr' ): return 'transformed' :param bool bufferKeys: If True (default), will add keys a frame before and after the range. ''' global TAGGING_ATTR global _loadAlterPlug existingSelection = selected() # Hook for easily providing an alterPlug via the GUI if _loadAlterPlug and not alterPlug: alterPlug = _loadAlterPlug # Using cmds for speed getAttr = cmds.getAttr objExists = cmds.objExists ls = cmds.ls # --- if insertTime is None: insertTime = currentTime(q=True) missingObj = set() missingAttr = [] pasteError = [] newNodes = cmds.file(filename, i=True, rnn=True) curves = cmds.ls(newNodes, type='animCurve') info = ls(newNodes, type='network')[0] start = getAttr(info + '.start') end = getAttr(info + '.end') length = end - start attr = '.' + TAGGING_ATTR singleObj = '' if len(existingSelection) == 1: targetObj = getAttr(curves[0] + attr).split('.')[0] for c in curves: loadedTarget = getAttr(c + attr).split('.')[0] # FKIK_SWITCH is a hack to deal with the switching attr if a single # obj is selected if loadedTarget != targetObj and not loadedTarget.endswith( 'FKIK_SWITCH'): break else: singleObj = targetObj if singleObj: targetObj = existingSelection[0].longName() def alter(plug): return targetObj + '.' + plug.split('.')[-1], None else: # Determine if there is a namespace mismatch if alterPlug: targets = [ alterPlug(cmds.getAttr(crv + attr))[0].split('.')[0] for crv in curves ] else: targets = [ cmds.getAttr(crv + attr).split('.')[0] for crv in curves ] changeNamespace = None newTargets = core.names.findAlternates(targets, targetPool) global JUNK JUNK = targets if newTargets.alteration: print('NS change', '--' * 20, newTargets.alteration) if newTargets.alteration[0] == 'add': def changeNamespace(plug): return newTargets.alteration[1] + plug elif newTargets.alteration[0] == 'sub': def changeNamespace(plug): return plug.replace(newTargets.alteration[1], newTargets.alteration[2]) elif newTargets.alteration[0] == 'rem': def changeNamespace(plug): return plug.replace(newTargets.alteration[1], '') # Build an alteration function if needed alter = None if alterPlug and changeNamespace: def alter(plug): newPlug, curveEditFunc = alterPlug(changeNamespace(plug)) return newPlug, curveEditFunc elif alterPlug: alter = alterPlug elif changeNamespace: def alter(plug): return changeNamespace(plug), None if hasAttr(PyNode(info), 'staticValues'): keys = json.loads( core.text.asciiDecompress(getAttr(info + '.staticValues'))) for plug, value in keys.items(): try: if alter: setAttr(alter(plug), value) else: setAttr(plug, value) except Exception: pass # Finally, actually copy over the animation for node in curves: alterCurve = None if objExists(node + '.' + TAGGING_ATTR): dest = getAttr(node + '.' + TAGGING_ATTR) if alter: dest, alterCurve = alter(dest) if alterCurve: alterCurve(node) if objExists(dest): # If we aren't going to be able to paste, just punt. if not getAttr(dest, k=True): pasteError.append(dest) continue if bufferKeys or getAttr(node, s=1) <= 1: setKeyframe(node, time=(insertTime - 1), insert=True) setKeyframe(node, time=(insertTime + length + 1), insert=True) copyKey(node, time=(start, end), iub=True, option='curve') try: pasteKey(dest, time=(insertTime, insertTime + length), option='replace') except Exception: pasteError.append(dest) else: obj, attr = dest.split('.') if objExists(obj): missingAttr.append(dest) else: missingObj.add(obj) if missingObj: print( core.text.writeInBox("These objects don't exist:\n\n" + '\n'.join(missingObj))) if missingAttr: print( core.text.writeInBox("These attribute couldn't be found:\n\n" + '\n'.join(missingAttr))) if pasteError: print( core.text.writeInBox( "Errors occurred when pasting animation onto:\n\n" + '\n'.join(pasteError))) if missingObj or missingAttr or pasteError: warning('Completed but with errors. See script editor for details.') delete(newNodes) return SavedCurveInfo(insertTime, insertTime + length, length)
def controllers(main=None): ''' Returns all the animation controllers in the scene. .. todo:: Add the shapes that have ik/fk switching on them ''' ''' Timing experiments: Results, cmds + sets is WAY faster! s = time.time() controls = [c for c in listRelatives(main, ad=True, type='transform') if c.hasAttr('fossilCtrlType')] print( time.time() - s, 'ls(*.fossilCtrlType) ORIGINAL' ) s = time.time() controls = [PyNode(c) for c in cmds.listRelatives(main.name(), ad=True, type='transform', f=1) if cmds.attributeQuery('fossilCtrlType', node=c, ex=True)] print( time.time() - s, 'cmds.listRelatives() list comp' ) s = time.time() #controls = [c for c in listRelatives(main, ad=True, type='transform') if c.hasAttr('fossilCtrlType')] allControls = set(cmds.ls('*.fossilCtrlType', type='transform', r=True, o=True)) allControls.intersection_update( cmds.listRelatives(main.name(), ad=True, type='transform') ) controls = [PyNode(o) for o in allControls] print( time.time() - s, 'cmds and set filtering' ) Swift Cat as main: 0.611000061035 ls(*.fossilCtrlType) ORIGINAL 0.123000144958 cmds.listRelatives() list comp 0.0439999103546 cmds and set filtering Swift as Main: 2.1210000515 ls(*.fossilCtrlType) ORIGINAL 0.398999929428 cmds.listRelatives() list comp 0.0769999027252 cmds and set filtering ''' allControls = set( cmds.ls( '*.fossilCtrlType', o=True, r=True ) ) if main: mainTransforms = cmds.listRelatives(main.name(), ad=True, type='transform') if mainTransforms: allControls.intersection_update( mainTransforms ) controls = [PyNode(o) for o in allControls] controls.append(main) root = rootMotion(main=main) if root: controls.append(root) return controls else: warning("No sub controls found for {0}".format(main.name())) else: controls = [PyNode(o) for o in allControls] main = mainGroup() if main: controls.append(main) root = rootMotion(main=main) if root: controls.append(root) return controls return []
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()) '''
def findAlternates(joints, available=None): ''' Given a list of objects, see if they all exist in the scene and try to figure out if they have lost or gained namespaces. :param list joints: A list of string names, not PyNodes. :param list available: If specified, only these objects will be considered valid targets, otherwise the whole scene will be considered. :return: None if unable to find matches for everything or a NSChange with the alterations made and list of new joints. NSCHange.alteration is a list with will be: * ['rem', <ns>] # The namespace was removed * ['add', <ns>] # The namespace was add * ['sub', <old>, <new>] # <old> was replaced with <new> ''' if available is not None: def objExists(obj): #print 'searching for', obj, type(obj) return obj in available else: objExists = pymel.core.objExists missing = [j for j in joints if not objExists(j)] fixed = not bool(missing) alteration = [] lowestFail = len(missing) bestChoice = None if missing: print('# missing %i / %i' % (len(missing), len(joints))) if not missing: return NSChange([], joints) else: for missingObj in missing: if missingObj.count(':'): # Try stripping off any namespace (accounting for long names) and proceed if all bones exist. namespace, simpleName = missingObj.split('|')[-1].rsplit( ':', 1) if objExists(simpleName): newJoints, failedCount = _remNS(joints, namespace) if newJoints: #weights = substitute( weights, [(namespace, '')] ) alteration = ['rem', namespace + ':'] fixed = True else: if failedCount < lowestFail: lowestFail = failedCount bestChoice = ['rem', namespace + ':'] if not fixed: #print 'Testing to see if joints exist with a different namespace l.m.fa' # See if only one obj exists in the scene with the # simple name and try adding that namespace if available: others = [ obj for obj in available if _add.simpleName(obj) == simpleName ] else: others = ls(simpleName, r=1) for other in others: newNamespace = _add.shortName(other).rsplit(':', 1)[0] newJoints, failedCount = _changeNS( joints, namespace, newNamespace) if newJoints: #weights = substitute( weights, [(namespace, newNamespace)] ) alteration = [ 'sub', namespace + ':', newNamespace + ':' ] fixed = True break else: if failedCount < lowestFail: lowestFail = failedCount bestChoice = [ 'sub', namespace + ':', newNamespace + ':' ] else: # See if the first bone has a namespace and try adding it # to all the others. others = cmds.ls(missingObj, r=1) for other in others: if available is not None and other not in available: continue namespace = other[:-len(missingObj)] newJoints, failedCount = _addNS(joints, namespace) if newJoints: #weights = prepend( weights, namespace ) alteration = ['add', namespace] fixed = True break else: if failedCount < lowestFail: lowestFail = failedCount bestChoice = ['add', namespace] if fixed: return NSChange(alteration, newJoints) if bestChoice: return NSChange(bestChoice, []) else: return NSChange([], [])
def _adjustForNamespace(jointNames): alts = cmds.ls( [name.rsplit(':', 1)[-1] for name in jointNames], r=True) if len(alts) == len(jointNames): return alts return []