def isValidNurbsCurve(shape): ''' Returns True if the given shape is valid as is not a sharedShape. ''' if attributeQuery( SHARED_SHAPE, node=shape, ex=True ): return False # Pymel barfs (at least in the past) on getting cv count on shared shape so we must use cmds return (cmds.getAttr( str(shape) + '.spans' ) and cmds.getAttr( str(shape) + '.degree' ))
def isValidNurbsCurve(shape): ''' Pymel barfs on getting cv count on shared shape so we must use cmds ''' if attributeQuery(sharedShapeTag, node=shape, ex=True): return False return (cmds.getAttr(str(shape) + '.spans') and cmds.getAttr(str(shape) + '.degree'))
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
def doIkFkSwitch(self, obj, isIk): mode, start, end = self.processRange() #ikFkSwitch(obj, start, end) print(mode, start, end) plug = controllerShape.getSwitcherPlug(obj) if isIk and cmds.getAttr(plug) == 1.0: print('Already IK, skipping') return elif not isIk and cmds.getAttr(plug) == 0.0: print('Already FK, skipping') return kinematicSwitch.ikFkSwitch(obj, start, end) print('doing switch')
def connect(obj, name_level): ''' Hook the given obj's visibility to the `name` attribute on the sharedShape. If the attr doesn't exist, it will be made. Optionanal `level` will determine when the `obj` will become visible. For example, 2 will not be visible at 1, but will at 2 and higher. ''' name, level = name_level # Probably should just update this eventually to be 3 params orig = obj zero = pdil.dagObj.zero(obj, apply=False, make=False) if zero: obj = zero shape = get() if not shape: warning( 'Unable to add vis control, no object exists named "main" or tagged with ".fossilMainControl"' ) return log.debug('Applying vis control to {}, was given {} using {}'.format( obj, orig, shape)) plug = shape + '.' + name if not cmds.objExists(plug): cmds.addAttr(shape, ln=name, at='short', min=0, max=level, dv=1) cmds.setAttr(shape + '.' + name, cb=True) elif cmds.getAttr(shape + '.' + name, type=True) not in [ 'bool', 'double', 'float', 'long', 'short' ]: warning( '{0} is not a good name for a vis group since the sharedShape has an attr already that is of the wrong type' .format(name)) return if cmds.addAttr(plug, q=True, max=True) < level: cmds.addAttr(plug, e=True, max=level) if level == 1: connectAttr(plug, obj.visibility.name(), f=True) else: connectAttr(getConditionNode(plug, level).outColorR, obj.visibility, f=True) obj.visibility.setKeyable(False) if not pdil.sharedShape.find(orig, VIS_NODE_TYPE): pdil.sharedShape.use(orig, shape) # If we have a main controller, put the container in a subgroup to make # the main group more organized. ''' 2021-11-25 Taking this out, I think redoing the hierarchery is probalby more confusing that having lots there.
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 _processAttr(plug, dups, forceKeys, staticValues, start, end): ''' Used by `save` ''' crvs = cmds.listConnections(plug, type='animCurve') if not crvs: if forceKeys: setKeyframe(plug, t=start) setKeyframe(plug, t=end) crvs = cmds.listConnections(plug, type='animCurve') else: if not cmds.getAttr(plug, lock=True) and not cmds.listConnections( plug, s=True, d=False): staticValues[plug] = cmds.getAttr(plug) if crvs: dup = cmds.duplicate(crvs)[0] if not objExists(dup + '.' + TAGGING_ATTR): cmds.addAttr(dup, ln=TAGGING_ATTR, dt='string') cmds.setAttr(dup + '.' + TAGGING_ATTR, plug, type='string') dups.append(dup)
def find(obj, shapeType): ''' If `obj` has shared shape of `shapeType`, return it or None if not found. ''' shapes = cmds.listRelatives(obj.name(), type='nurbsCurve', f=True) if not shapes: return None for shape in shapes: if (cmds.attributeQuery(core.shape.SHARED_SHAPE, n=shape, ex=True) and cmds.getAttr(shape + '.' + core.shape.SHARED_SHAPE) == shapeType): return shape return None
def use(obj, shapeNode): ''' Have the given obj use the sharedShape. ''' if find(obj, cmds.getAttr(shapeNode + '.' + core.shape.SHARED_SHAPE)): return try: cmds.parent(shapeNode, obj.name(), add=True, shape=True) except RuntimeError as ex: ''' cmds errors are a pain to catch and I really only want to catch if it's already a parent but th ''' if ex.args != ('Maya command error', ): raise
def connect(obj, name): ''' Hook the given obj's visibility to the `name` attribute on the sharedShape. If the attr doesn't exist, it will be made. ''' orig = obj zero = core.dagObj.zero(obj, apply=False, make=False) if zero: obj = zero shape = get() plug = shape + '.' + name if not cmds.objExists(plug): cmds.addAttr(shape, ln=name, at='short', min=0, max=1, dv=1) cmds.setAttr(shape + '.' + name, cb=True) elif cmds.getAttr(shape + '.' + name, type=True) not in [ 'bool', 'double', 'float', 'long', 'short' ]: warning( '{0} is not a good name for a vis group since the sharedShape has an attr already that is of the wrong type' .format(name)) return connectAttr(plug, obj.visibility.name(), f=True) obj.visibility.setKeyable(False) # If we have a main controller, put the container in a subgroup to make # the main group more organized. visGroupName = '_vis_' + name if isinstance(orig, nodeApi.RigController): if shortName(orig.container.getParent()) != visGroupName: orig.setGroup(visGroupName)
Optionanal `level` will determine when the `obj` will become visible. For example, 2 will not be visible at 1, but will at 2 and higher. ''' orig = obj zero = core.dagObj.zero(obj, apply=False, make=False) if zero: obj = zero shape = get() plug = shape + '.' + name if not cmds.objExists( plug ): cmds.addAttr( shape, ln=name, at='short', min=0, max=level, dv=1 ) cmds.setAttr( shape + '.' + name, cb=True ) elif cmds.getAttr( shape + '.' + name, type=True) not in ['bool', 'double', 'float', 'long', 'short']: warning( '{0} is not a good name for a vis group since the sharedShape has an attr already that is of the wrong type'.format(name) ) return if cmds.addAttr(plug, q=True, max=True) < level: cmds.addAttr(plug, e=True, max=level) if level == 1: connectAttr( plug, obj.visibility.name(), f=True) else: connectAttr( getConditionNode(plug, level).outColorR, obj.visibility, f=True) obj.visibility.setKeyable(False) # If we have a main controller, put the container in a subgroup to make # the main group more organized.
def animationSwitchMenu(objName): ''' Called from dagMenuProc() so it's wrapped to catch any error. ''' try: obj = PyNode(objName) plug = controllerShape.getSwitcherPlug(obj) spaces = space.getNames(obj) #------- # Mode #------- if plug or spaces: def setMode(mode): animToolSettings.switchMode = mode menuItem(l='Mode: Current Frame', c=Callback(setMode, 'current'), cb=eq(animToolSettings.switchMode, 'current') ) # noqa e241 menuItem(l='Mode: Playback Range', c=Callback(setMode, 'range'), cb=eq(animToolSettings.switchMode, 'range') ) # noqa e241 menuItem(l='Mode: All', c=Callback(setMode, 'all'), cb=eq(animToolSettings.switchMode, 'all') ) # noqa e241 #------- # Ik/Fk #------- if plug: if cmds.getAttr(obj + '.fossilCtrlType') in ['translate', 'rotate']: destType = 'Ik' else: destType = 'Fk' if core.time.rangeIsSelected(): s, e = core.time.selectedTime() elif animToolSettings.switchMode == 'current': s, e = [currentTime(q=1)] * 2 elif animToolSettings.switchMode == 'range': s, e = core.time.playbackRange() elif animToolSettings.switchMode == 'all': s, e = None, None ''' The dag menu can be triggered: * Object is selected but right click is on nothing * Object is selected but right click is on another object * Nothing is selected right clicking over an object Therefore it's a bunch of work to figure out if several things should be considered or not. ''' sel = selected() if len(sel) <= 1 and (sel[0] == obj if sel else True): menuItem(l='Switch to ' + destType, c=core.alt.Callback(kinematicSwitch.ikFkSwitch, obj, s, e)) else: sel = set(sel) sel.add(obj) switches = {} for o in sel: switchPlug = controllerShape.getSwitcherPlug(o) switches[ switchPlug.rsplit('|')[-1] ] = o if len(switches) == 1: menuItem(l='Switch to ' + destType, c=core.alt.Callback(kinematicSwitch.ikFkSwitch, obj, s, e)) else: menuItem(l='Switch mutliple', c=core.alt.Callback(kinematicSwitch.multiSwitch, switches.values(), s, e)) #------- # Spaces #------- if spaces: objsToSwitch = [obj] # Check if other selected objects have spaces to possibly swich to. sel = selected() if obj not in sel: sel.append(obj) if len(sel) > 1: allSpaces = [] for o in sel: tempSpaces = space.getNames(o) if tempSpaces: allSpaces.append(tempSpaces) if len(allSpaces) > 1: objsToSwitch = sel spaces = set(allSpaces[0]).intersection( allSpaces[1] ) for t in allSpaces[2:]: spaces.intersection_update(t) if spaces: menuItem(l='Switch space from %s to' % obj.space.get(asString=True), sm=True) for _space in sorted(spaces): menuItem(l=_space, c=Callback(switchSpaceGroup, objsToSwitch, _space)) setParent('..', m=True) #------- # Main #------- """ if lib.dagObj.simpleName(obj) == 'main': isMain = True if objExists(CONSTRAINT_SET_NAME): if PyNode(CONSTRAINT_SET_NAME).elements(): menuItem(l='Main Control Re-Lock', c=Callback(relockMain)) else: menuItem(l='Main Control Unlock', c=Callback(unlockMain)) else: menuItem(l='Main Control Unlock', c=Callback(unlockMain)) menuItem(l='Main Zero', sm=True) menuItem(l='All') for attr in [trans + axis for trans in 'tr' for axis in 'xyz']: skip = [trans + axis for trans in 'tr' for axis in 'xyz'] skip.remove(attr) menuItem(l='Zero ' + attr, c=Callback(resetMain, skip)) setParent('..', m=True) else: isMain = False # Divider, if needed """ if plug or spaces: menuItem(d=True) except Exception: print( traceback.format_exc() )
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 animationSwitchMenu(objName): ''' Called from dagMenuProc() so it's wrapped to catch any error. ''' try: obj = PyNode(objName) plug = controller.getSwitcherPlug(obj) spaces = space.getNames(obj) #------- # Mode #------- if plug or spaces: def setMode(mode): animToolSettings.switchMode = mode menuItem(l='Mode: Current Frame', c=Callback(setMode, 'current'), cb=eq(animToolSettings.switchMode, 'current')) # noqa e241 menuItem(l='Mode: Playback Range', c=Callback(setMode, 'range'), cb=eq(animToolSettings.switchMode, 'range')) # noqa e241 menuItem(l='Mode: All', c=Callback(setMode, 'all'), cb=eq(animToolSettings.switchMode, 'all')) # noqa e241 #------- # Ik/Fk #------- if plug: if cmds.getAttr(obj + '.motigaCtrlType') in ['translate', 'rotate']: destType = 'Ik' else: destType = 'Fk' if core.time.rangeIsSelected(): s, e = core.time.selectedTime() elif animToolSettings.switchMode == 'current': s, e = [currentTime(q=1)] * 2 elif animToolSettings.switchMode == 'range': s, e = core.time.playbackRange() elif animToolSettings.switchMode == 'all': s, e = None, None menuItem(l='Switch to ' + destType, c=core.alt.Callback(kinematicSwitch.ikFkSwitch, obj, s, e)) #------- # Spaces #------- if spaces: objsToSwitch = [obj] # Check if other selected objects have spaces to possibly swich to. sel = selected() if obj not in sel: sel.append(obj) if len(sel) > 1: allSpaces = [] for o in sel: tempSpaces = space.getNames(o) if tempSpaces: allSpaces.append(tempSpaces) if len(allSpaces) > 1: objsToSwitch = sel spaces = set(allSpaces[0]).intersection(allSpaces[1]) for t in allSpaces[2:]: spaces.intersection_update(t) if spaces: menuItem(l='Switch space from %s to' % obj.space.get(asString=True), sm=True) for _space in sorted(spaces): menuItem(l=_space, c=Callback(switchSpaceGroup, objsToSwitch, _space)) setParent('..', m=True) #------- # Main #------- """ if lib.dagObj.simpleName(obj) == 'main': isMain = True if objExists(CONSTRAINT_SET_NAME): if PyNode(CONSTRAINT_SET_NAME).elements(): menuItem(l='Main Control Re-Lock', c=Callback(relockMain)) else: menuItem(l='Main Control Unlock', c=Callback(unlockMain)) else: menuItem(l='Main Control Unlock', c=Callback(unlockMain)) menuItem(l='Main Zero', sm=True) menuItem(l='All') for attr in [trans + axis for trans in 'tr' for axis in 'xyz']: skip = [trans + axis for trans in 'tr' for axis in 'xyz'] skip.remove(attr) menuItem(l='Zero ' + attr, c=Callback(resetMain, skip)) setParent('..', m=True) else: isMain = False # Divider, if needed if plug or len(spaces) > 1 or isMain: menuItem(d=True) """ except Exception: print(traceback.format_exc())
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)