def replaceGivenConstraintTarget(constraint, targetToReplace, newTarget): ''' replaces targetToReplace transform on the given constraint with the newTarget transform ''' targetToReplace = apiExtensions.asMObject(targetToReplace) newTarget = apiExtensions.asMObject(newTarget) #nothing to do if the nodes are the same... if apiExtensions.cmpNodes(targetToReplace, newTarget): return usedTargetIndices = getAttr('%s.target' % constraint, multiIndices=True) for idx in usedTargetIndices: for attr in attributeQuery('target', node=constraint, listChildren=True): for connection in listConnections('%s.target[%s].%s' % (constraint, idx, attr), p=True, type='transform', d=False) or []: toks = connection.split('.') node = toks[0] if apiExtensions.cmpNodes(node, targetToReplace): toks[0] = str(newTarget) connectAttr('.'.join(toks), '%s.target[%s].%s' % (constraint, idx, attr), f=True)
def connect(self, obj, slot=None): ''' performs the actual connection of an object to a connect slot ''' if not cmd.objExists(obj): return -1 #if the user is trying to connect the trigger to itself, return zero which is the reserved slot for the trigger if apiExtensions.cmpNodes(self.obj, obj): return 0 if slot is None: slot = self.nextSlot() if slot <= 0: return 0 #make sure the connect isn't already connected - if it is, return the slot number existingSlots = self.isConnected(obj) if existingSlots: return self.getConnectSlots(obj)[0] conPrefix = 'zooTrig' prefixSize = len(conPrefix) slotPath = "%s.%s%d" % (self.obj, conPrefix, slot) if not objExists(slotPath): cmd.addAttr(self.obj, ln="%s%d" % (conPrefix, slot), at='message') cmd.connectAttr("%s.msg" % obj, slotPath, f=True) self.cacheConnect(slot) return slot
def connect( self, obj, slot=None ): ''' performs the actual connection of an object to a connect slot ''' if not cmd.objExists(obj): return -1 #if the user is trying to connect the trigger to itself, return zero which is the reserved slot for the trigger if apiExtensions.cmpNodes( self.obj, obj ): return 0 if slot is None: slot = self.nextSlot() if slot <= 0: return 0 #make sure the connect isn't already connected - if it is, return the slot number existingSlots = self.isConnected(obj) if existingSlots: return self.getConnectSlots(obj)[0] conPrefix = 'zooTrig' prefixSize = len(conPrefix) slotPath = "%s.%s%d" % (self.obj, conPrefix, slot) if not objExists( slotPath ): cmd.addAttr(self.obj,ln= "%s%d" % (conPrefix, slot), at='message') cmd.connectAttr( "%s.msg" % obj, slotPath, f=True ) self.cacheConnect( slot ) return slot
def isSingular(self): if self.controlB is None: return True #a pair is also singular if controlA is the same as controlB #NOTE: cmpNodes does a rigorous comparison so it will catch a fullpath and a partial path that point to the same node if apiExtensions.cmpNodes(self.controlA, self.controlB): return True return False
def isSingular(self): if self.controlB is None: return True # a pair is also singular if contorlA is the same as controlB # NOTE: cmpNodes does a rigorous comparison so it will catch a fullpath and a partial path that point to the same node if apiExtensions.cmpNodes(self.controlA, self.controlB): return True return False
def Create(cls, controlA, controlB=None, axis=None): ''' given two controls will setup the relationship between them NOTE: if controlB isn't given then it will only be able to mirror its current pose. This is usually desirable on "central" controls like spine, head and neck controls ''' #make sure we've been given transforms - mirroring doesn't make a whole lotta sense on non-transforms if not objectType(controlA, isAType='transform'): return None if controlB: #if controlA is the same node as controlB then set controlB to None - this makes it more obvious the pair is singular #NOTE: cmpNodes compares the actual MObjects, not the node names - just in case we've been handed a full path and a partial path that are the same node... if apiExtensions.cmpNodes(controlA, controlB): controlB = None elif not objectType(controlB, isAType='transform'): return None #see if we have a pair node for the controls already pairNode = cls.GetPairNode(controlA) if pairNode: #if no controlB has been given see whether the pairNode we've already got also has no controlB - if so, we're done if not controlB: new = cls(pairNode) if not new.controlB: return new #if controlB HAS been given, check whether to see whether it has the same pairNode - if so, we're done if controlB: pairNodeB = cls.GetPairNode(controlB) if pairNode == pairNodeB: return cls(pairNode) #otherwise create a new one pairNode = createNode('controlPair') connectAttr('%s.message' % controlA, '%s.controlA' % pairNode) if controlB: connectAttr('%s.message' % controlB, '%s.controlB' % pairNode) #name the node nodeName = '%s_mirrorConfig' if controlB is None else '%s_%s_exchangeConfig' % ( controlA, controlB) pairNode = rename(pairNode, nodeName) #instantiate it and run the initial setup code over it new = cls(pairNode) new.setup(axis) return new
def replaceGivenConstraintTarget( constraint, targetToReplace, newTarget ): ''' replaces targetToReplace transform on the given constraint with the newTarget transform ''' targetToReplace = apiExtensions.asMObject( targetToReplace ) newTarget = apiExtensions.asMObject( newTarget ) #nothing to do if the nodes are the same... if apiExtensions.cmpNodes( targetToReplace, newTarget ): return usedTargetIndices = getAttr( '%s.target' % constraint, multiIndices=True ) for idx in usedTargetIndices: for attr in attributeQuery( 'target', node=constraint, listChildren=True ): for connection in listConnections( '%s.target[%s].%s' % (constraint, idx, attr), p=True, type='transform', d=False ) or []: toks = connection.split( '.' ) node = toks[ 0 ] if apiExtensions.cmpNodes( node, targetToReplace ): toks[ 0 ] = str( newTarget ) connectAttr( '.'.join( toks ), '%s.target[%s].%s' % (constraint, idx, attr), f=True )
def replaceConstraintTarget( constraint, newTarget, targetIndex=0 ): ''' replaces the target at "targetIndex" with the new target ''' newTarget = apiExtensions.asMObject( str( newTarget ) ) for attr in attributeQuery( 'target', node=constraint, listChildren=True ): for connection in listConnections( '%s.target[%s].%s' % (constraint, targetIndex, attr), p=True, type='transform', d=False ) or []: toks = connection.split( '.' ) node = toks[ 0 ] if not apiExtensions.cmpNodes( node, newTarget ): toks[ 0 ] = str( newTarget ) connectAttr( '.'.join( toks ), '%s.target[%s].%s' % (constraint, targetIndex, attr), f=True )
def getConnectSlots(self, obj): ''' return a list of the connect slot indicies the given obj is connected to ''' if apiExtensions.cmpNodes(self.obj, obj): return [0] conPrefix = 'zooTrig' prefixSize = len(conPrefix) slots = set() connections = cmd.listConnections(obj + '.msg', s=False, p=True) or [] for con in connections: obj, attr = con.split('.') if not apiExtensions.cmpNodes(obj, self.obj): continue slot = attr[prefixSize:] if attr.startswith(conPrefix) and slot.isdigit(): slots.add(int(slot)) #we need to check against all the cache attributes to see if the object exists but has been disconnected somehow allSlots = self.connects() getAttr = cmd.getAttr for connect, slot in allSlots: cacheAttrpath = '%s.%s%dcache' % (self.obj, conPrefix, slot) if objExists(cacheAttrpath): cacheValue = getAttr(cacheAttrpath) if cacheValue == obj: slots.add(slot) slots = list(slots) slots.sort() return slots
def getConnectSlots( self, obj ): ''' return a list of the connect slot indicies the given obj is connected to ''' if apiExtensions.cmpNodes( self.obj, obj ): return [0] conPrefix = 'zooTrig' prefixSize = len( conPrefix ) slots = set() connections = cmd.listConnections( obj +'.msg', s=False, p=True ) or [] for con in connections: obj, attr = con.split('.') if not apiExtensions.cmpNodes( obj, self.obj ): continue slot = attr[ prefixSize: ] if attr.startswith(conPrefix) and slot.isdigit(): slots.add( int(slot) ) #we need to check against all the cache attributes to see if the object exists but has been disconnected somehow allSlots = self.connects() getAttr = cmd.getAttr for connect, slot in allSlots: cacheAttrpath = '%s.%s%dcache' % (self.obj, conPrefix, slot) if objExists( cacheAttrpath ): cacheValue = getAttr( cacheAttrpath ) if cacheValue == obj: slots.add( slot ) slots = list( slots ) slots.sort() return slots
def getChain( startNode, endNode ): ''' returns a list of all the joints from the given start to the end inclusive ''' chainNodes = [ endNode ] for p in apiExtensions.iterParents( endNode ): if not p: raise ValueError( "Chain terminated before reaching the end node!" ) chainNodes.append( p ) if apiExtensions.cmpNodes( p, startNode ): #cmpNodes is more reliable than just string comparing - cmpNodes casts to MObjects and compares object handles break chainNodes.reverse() return chainNodes
def getChain( startNode, endNode ): ''' returns a list of all the joints from the given start to the end inclusive ''' chainNodes = [ endNode ] for p in api.iterParents( endNode ): if not p: raise ValueError( "Chain terminated before reaching the end node!" ) chainNodes.append( p ) if apiExtensions.cmpNodes( p, startNode ): #cmpNodes is more reliable than just string comparing - cmpNodes casts to MObjects and compares object handles break chainNodes.reverse() return chainNodes
def chainLength( startNode, endNode ): ''' measures the length of the chain were it to be straightened out ''' length = 0 curNode = endNode for p in api.iterParents( endNode ): curPos = Vector( xform( curNode, q=True, ws=True, rp=True ) ) parPos = Vector( xform( p, q=True, ws=True, rp=True ) ) dif = curPos - parPos length += dif.get_magnitude() if apiExtensions.cmpNodes( p, startNode ): #cmpNodes is more reliable than just string comparing - cmpNodes casts to MObjects and compares object handles break curNode = p return length
def chainLength( startNode, endNode ): ''' measures the length of the chain were it to be straightened out ''' length = 0 curNode = endNode for p in apiExtensions.iterParents( endNode ): curPos = Vector( xform( curNode, q=True, ws=True, rp=True ) ) parPos = Vector( xform( p, q=True, ws=True, rp=True ) ) dif = curPos - parPos length += dif.get_magnitude() if apiExtensions.cmpNodes( p, startNode ): #cmpNodes is more reliable than just string comparing - cmpNodes casts to MObjects and compares object handles break curNode = p return length
def add(src, tgt, name=None, space=None, maintainOffset=True, nodeWithParentAttr=None, skipTranslationAxes=(), skipRotationAxes=(), constraintType=CONSTRAINT_PARENT): global AXES AXES = list(AXES) if space is None: space = listRelatives(src, p=True, pa=True)[0] if nodeWithParentAttr is None: nodeWithParentAttr = src if not name: name = getNiceName(tgt) if name is None: name = camelCaseToNice(str(tgt)) #if there is an existing constraint, check to see if the target already exists in its target list - if it does, return the condition used it uses attrState(space, ('t', 'r'), lock=False) existingConstraint = findConstraint(src) if existingConstraint: constraintType = nodeType(existingConstraint) constraintFunc = getattr(cmd, constraintType) targetsOnConstraint = constraintFunc(existingConstraint, q=True, tl=True) if tgt in targetsOnConstraint: idx = targetsOnConstraint.index(tgt) aliases = constraintFunc(existingConstraint, q=True, weightAliasList=True) cons = listConnections('%s.%s' % (existingConstraint, aliases[idx]), type='condition', d=False) return cons[0] #when skip axes are specified maya doesn't handle things properly - so make sure #ALL transform channels are connected, and remove unwanted channels at the end... preT, preR = getAttr('%s.t' % space)[0], getAttr('%s.r' % space)[0] if existingConstraint: chans = CONSTRAINT_CHANNELS[constraintType] for channel, constraintAttr in zip(*chans): for axis in AXES: spaceAttr = '%s.%s%s' % (space, channel, axis) conAttr = '%s.%s%s' % (existingConstraint, constraintAttr, axis) if not isConnected(conAttr, spaceAttr): connectAttr(conAttr, spaceAttr) #get the names for the parents from the parent enum attribute cmdOptionKw = {'mo': True} if maintainOffset else {} if objExists('%s.parent' % nodeWithParentAttr): srcs, names = getSpaceTargetsNames(src) addAttr('%s.parent' % nodeWithParentAttr, e=True, enumName=':'.join(names + [name])) #if we're building a pointConstraint instead of a parent constraint AND we already #have spaces on the object, we need to turn the -mo flag off regardless of what the #user set it to, as the pointConstraint maintain offset has different behaviour to #the parent constraint if constraintType in (CONSTRAINT_POINT, CONSTRAINT_ORIENT): cmdOptionKw = {} else: addAttr(nodeWithParentAttr, ln='parent', at="enum", en=name) setAttr('%s.parent' % nodeWithParentAttr, keyable=True) #now build the constraint constraintFunction = getattr(cmd, constraintType) constraint = constraintFunction(tgt, space, **cmdOptionKw)[0] weightAliasList = constraintFunction(constraint, q=True, weightAliasList=True) targetCount = len(weightAliasList) constraintAttr = weightAliasList[-1] condition = shadingNode('condition', asUtility=True, n='%s_to_space_%s#' % (getShortName(src), getShortName(tgt))) setAttr('%s.secondTerm' % condition, targetCount - 1) setAttr('%s.colorIfTrue' % condition, 1, 1, 1) setAttr('%s.colorIfFalse' % condition, 0, 0, 0) connectAttr('%s.parent' % nodeWithParentAttr, '%s.firstTerm' % condition) connectAttr('%s.outColorR' % condition, '%s.%s' % (constraint, constraintAttr)) #find out what symbol to use to find the parent attribute parentAttrIdx = 0 if not apiExtensions.cmpNodes(space, src): parentAttrIdx = triggered.addConnect(src, nodeWithParentAttr) #add the zooObjMenu commands to the object for easy space switching Trigger.CreateMenu(src, "parent to %s" % name, ChangeSpaceCmd.Create(targetCount - 1, parentAttrIdx)) #when skip axes are specified maya doesn't handle things properly - so make sure #ALL transform channels are connected, and remove unwanted channels at the end... for axis, value in zip(AXES, preT): if axis in skipTranslationAxes: attr = '%s.t%s' % (space, axis) delete(attr, icn=True) setAttr(attr, value) for axis, value in zip(AXES, preR): if axis in skipRotationAxes: attr = '%s.r%s' % (space, axis) delete(attr, icn=True) setAttr(attr, value) #make the space node non-keyable and lock visibility attrState(space, ['t', 'r', 's'], lock=True) attrState(space, 'v', *control.HIDE) return condition
def add( src, tgt, name=None, space=None, maintainOffset=True, nodeWithParentAttr=None, skipTranslationAxes=(), skipRotationAxes=(), constraintType=CONSTRAINT_PARENT ): global AXES AXES = list( AXES ) if space is None: space = listRelatives( src, p=True, pa=True )[ 0 ] if nodeWithParentAttr is None: nodeWithParentAttr = src if not name: name = getNiceName( tgt ) if name is None: name = camelCaseToNice( str( tgt ) ) #if there is an existing constraint, check to see if the target already exists in its target list - if it does, return the condition used it uses attrState( space, ('t', 'r'), lock=False ) existingConstraint = findConstraint( src ) if existingConstraint: constraintType = nodeType( existingConstraint ) constraintFunc = getattr( cmd, constraintType ) targetsOnConstraint = constraintFunc( existingConstraint, q=True, tl=True ) if tgt in targetsOnConstraint: idx = targetsOnConstraint.index( tgt ) aliases = constraintFunc( existingConstraint, q=True, weightAliasList=True ) cons = listConnections( '%s.%s' % (existingConstraint, aliases[ idx ]), type='condition', d=False ) return cons[ 0 ] #when skip axes are specified maya doesn't handle things properly - so make sure #ALL transform channels are connected, and remove unwanted channels at the end... preT, preR = getAttr( '%s.t' % space )[0], getAttr( '%s.r' % space )[0] if existingConstraint: chans = CONSTRAINT_CHANNELS[ constraintType ] for channel, constraintAttr in zip( *chans ): for axis in AXES: spaceAttr = '%s.%s%s' %( space, channel, axis) conAttr = '%s.%s%s' % (existingConstraint, constraintAttr, axis) if not isConnected( conAttr, spaceAttr ): connectAttr( conAttr, spaceAttr ) #get the names for the parents from the parent enum attribute cmdOptionKw = { 'mo': True } if maintainOffset else {} if objExists( '%s.parent' % nodeWithParentAttr ): srcs, names = getSpaceTargetsNames( src ) addAttr( '%s.parent' % nodeWithParentAttr, e=True, enumName=':'.join( names + [name] ) ) #if we're building a pointConstraint instead of a parent constraint AND we already #have spaces on the object, we need to turn the -mo flag off regardless of what the #user set it to, as the pointConstraint maintain offset has different behaviour to #the parent constraint if constraintType in ( CONSTRAINT_POINT, CONSTRAINT_ORIENT ): cmdOptionKw = {} else: addAttr( nodeWithParentAttr, ln='parent', at="enum", en=name ) setAttr( '%s.parent' % nodeWithParentAttr, keyable=True ) #now build the constraint constraintFunction = getattr( cmd, constraintType ) constraint = constraintFunction( tgt, space, **cmdOptionKw )[ 0 ] weightAliasList = constraintFunction( constraint, q=True, weightAliasList=True ) targetCount = len( weightAliasList ) constraintAttr = weightAliasList[ -1 ] condition = shadingNode( 'condition', asUtility=True ) condition = rename( condition, '%s_to_space_%s#' % (cleanShortName( src ), cleanShortName( tgt )) ) setAttr( '%s.secondTerm' % condition, targetCount-1 ) setAttr( '%s.colorIfTrue' % condition, 1, 1, 1 ) setAttr( '%s.colorIfFalse' % condition, 0, 0, 0 ) connectAttr( '%s.parent' % nodeWithParentAttr, '%s.firstTerm' % condition ) connectAttr( '%s.outColorR' % condition, '%s.%s' % (constraint, constraintAttr) ) #find out what symbol to use to find the parent attribute parentAttrIdx = 0 if not apiExtensions.cmpNodes( space, src ): parentAttrIdx = triggered.addConnect( src, nodeWithParentAttr ) #add the zooObjMenu commands to the object for easy space switching Trigger.CreateMenu( src, "parent to %s" % name, ChangeSpaceCmd.Create( targetCount-1, parentAttrIdx ) ) #when skip axes are specified maya doesn't handle things properly - so make sure #ALL transform channels are connected, and remove unwanted channels at the end... for axis, value in zip( AXES, preT ): if axis in skipTranslationAxes: attr = '%s.t%s' % (space, axis) delete( attr, icn=True ) setAttr( attr, value ) for axis, value in zip( AXES, preR ): if axis in skipRotationAxes: attr = '%s.r%s' % (space, axis) delete( attr, icn=True ) setAttr( attr, value ) #make the space node non-keyable and lock visibility attrState( space, [ 't', 'r', 's' ], lock=True ) attrState( space, 'v', *control.HIDE ) return condition
def doBuild(self, bicep, elbow, wrist, nameScheme=ARM_NAMING_SCHEME, alignEnd=True, **kw): idx = kw['idx'] scale = kw['scale'] parity = Parity(idx) suffix = parity.asName() worldPart = WorldPart.Create() worldControl = worldPart.control partsControl = worldPart.parts colour = ColourDesc('green') if parity == Parity.LEFT else ColourDesc( 'red') #grab a list of 'bicep joints' - these are the child joints of the bicep that aren't the elbow or any of its #children. these joints are usually involved in deformation related to the bicep so we want to capture them #to use for geometry extraction for the control representation bicepJoints = [bicep] for child in listRelatives(bicep, pa=True, type='joint') or []: if cmpNodes(child, elbow): continue bicepJoints.append(child) bicepJoints += listRelatives(child, type='joint', pa=True, ad=True) or [] #grab the 'elbow joints' as per the above description elbowJoints = [elbow] for child in listRelatives(elbow, pa=True, type='joint') or []: if cmpNodes(child, wrist): continue elbowJoints.append(child) elbowJoints += listRelatives(child, type='joint', pa=True, ad=True) or [] #print 'THE BIPS', bicepJoints #print 'THE BOWS', elbowJoints ### BUILD THE FK CONTROLS ikArmSpace = buildAlignedNull(wrist, "ik_%sSpace%s" % (nameScheme[0], suffix), parent=worldControl) fkArmSpace = buildAlignedNull(bicep, "fk_%sSpace%s" % (nameScheme[0], suffix)) BONE_AXIS = AIM_AXIS + 3 if parity else AIM_AXIS driverUpper = buildControl("fk_%sControl%s" % (nameScheme[1], suffix), bicep, PivotModeDesc.MID, Shape_Skin(bicepJoints, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, scale=scale, parent=fkArmSpace) driverMid = buildControl("fk_%sControl%s" % (nameScheme[2], suffix), elbow, PivotModeDesc.MID, Shape_Skin(elbowJoints, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, scale=scale, parent=driverUpper) driverLower = buildControl("fk_%sControl%s" % (nameScheme[3], suffix), PlaceDesc(wrist, wrist if alignEnd else None), shapeDesc=Shape_Skin(wrist, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, constrain=False, scale=scale) #don't parent the driverLower in the buildControl command otherwise the control won't be in worldspace parent(driverLower, driverMid) makeIdentity(driverLower) ### BUILD THE POLE CONTROL polePos = rigUtils.findPolePosition(driverLower, driverMid, driverUpper, 5) poleControl = buildControl("%s_poleControl%s" % (nameScheme[0], suffix), PlaceDesc(elbow, PlaceDesc.WORLD), shapeDesc=ShapeDesc('sphere', None), colour=colour, constrain=False, parent=worldControl, scale=scale * 0.5) poleControlSpace = getNodeParent(poleControl) attrState(poleControlSpace, 'v', lock=False, show=True) move(polePos[0], polePos[1], polePos[2], poleControlSpace, a=True, ws=True, rpr=True) move(polePos[0], polePos[1], polePos[2], poleControl, a=True, ws=True, rpr=True) makeIdentity(poleControlSpace, a=True, t=True) setAttr('%s.v' % poleControl, False) ### BUILD THE POLE SELECTION TRIGGER lineNode = buildControl("%s_poleSelectionTrigger%s" % (nameScheme[0], suffix), shapeDesc=ShapeDesc('sphere', None), colour=ColourDesc('darkblue'), scale=scale, constrain=False, oriented=False, parent=ikArmSpace) lineStart, lineEnd, lineShape = buildAnnotation(lineNode) parent(lineStart, poleControl) delete(pointConstraint(poleControl, lineStart)) pointConstraint(elbow, lineNode) attrState(lineNode, ('t', 'r'), *LOCK_HIDE) setAttr('%s.template' % lineStart, 1) #make the actual line unselectable #build the IK handle ikHandle = cmd.ikHandle(fs=1, sj=driverUpper, ee=driverLower, solver='ikRPsolver')[0] limbControl = buildControl('%sControl%s' % (nameScheme[0], suffix), PlaceDesc(wrist, wrist if alignEnd else None), shapeDesc=Shape_Skin(wrist, axis=BONE_AXIS), colour=colour, scale=scale, constrain=False, parent=ikArmSpace) xform(limbControl, p=True, rotateOrder='yzx') setAttr('%s.snapEnable' % ikHandle, False) setAttr('%s.v' % ikHandle, False) addAttr(limbControl, ln='ikBlend', shortName='ikb', dv=1, min=0, max=1, at='double') setAttr('%s.ikBlend' % limbControl, keyable=True) connectAttr('%s.ikBlend' % limbControl, '%s.ikBlend' % ikHandle) attrState(ikHandle, 'v', *LOCK_HIDE) parent(ikHandle, partsControl) parentConstraint(limbControl, ikHandle) #parent( ikHandle, limbControl ) # poleVectorConstraint(poleControl, ikHandle) #setup constraints to the wrist - it is handled differently because it needs to blend between the ik and fk chains (the other controls are handled by maya) wristOrient = buildAlignedNull(wrist, "%s_follow%s" % (nameScheme[3], suffix), parent=partsControl) pointConstraint(driverLower, wrist) orientConstraint(wristOrient, wrist, mo=True) setItemRigControl(wrist, wristOrient) wristSpaceOrient = parentConstraint(limbControl, wristOrient, weight=0, mo=True)[0] wristSpaceOrient = parentConstraint(driverLower, wristOrient, weight=0, mo=True)[0] setAttr('%s.interpType' % wristSpaceOrient, 2) #connect the ikBlend of the arm controller to the orient constraint of the fk wrist - ie turn it off when ik is off... weightRevNode = shadingNode('reverse', asUtility=True) wristOrientAttrs = listAttr(wristSpaceOrient, ud=True) connectAttr('%s.ikBlend' % limbControl, '%s.inputX' % weightRevNode, f=True) connectAttr('%s.ikBlend' % limbControl, '%s.%s' % (wristSpaceOrient, wristOrientAttrs[0]), f=True) connectAttr('%s.outputX' % weightRevNode, '%s.%s' % (wristSpaceOrient, wristOrientAttrs[1]), f=True) #build expressions for fk blending and control visibility fkVisCond = shadingNode('condition', asUtility=True) poleVisCond = shadingNode('condition', asUtility=True) connectAttr('%s.ikBlend' % limbControl, '%s.firstTerm' % fkVisCond, f=True) connectAttr('%s.ikBlend' % limbControl, '%s.firstTerm' % poleVisCond, f=True) connectAttr('%s.outColorR' % fkVisCond, '%s.v' % driverUpper, f=True) connectAttr('%s.outColorG' % poleVisCond, '%s.v' % poleControlSpace, f=True) connectAttr('%s.outColorG' % poleVisCond, '%s.v' % limbControl, f=True) setAttr('%s.secondTerm' % fkVisCond, 1) #add set pole to fk pos command to pole control fkControls = driverUpper, driverMid, driverLower poleTrigger = Trigger(poleControl) poleConnectNums = [poleTrigger.connect(c) for c in fkControls] idx_toFK = poleTrigger.setMenuInfo( None, "move to FK position", 'zooVectors;\nfloat $pos[] = `zooFindPolePosition "-start %%%s -mid %%%s -end %%%s"`;\nmove -rpr $pos[0] $pos[1] $pos[2] #;' % tuple(poleConnectNums)) poleTrigger.setMenuInfo( None, "move to FK pos for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\\"#\\\",(zooGetObjMenuCmdStr(\\\"#\\\",%%%d)),{}))";' % idx_toFK) ##build the post trace commands for the pole vectors - once they've been placed after a trace, its safe and almost always ##desireable to place the pole vectors a little more sensibly #zooSetPostTraceCmd $poleControl ( "zooVectors; zooPlacePole \"-obj # -start %"+ $poleConnectNums[0] +" -mid %"+ $poleConnectNums[1] +" -end %"+ $poleConnectNums[2] +" -key 1 -removeKey 1 -invalidMode 1\";" ); #add IK/FK switching commands limbTrigger = Trigger(limbControl) handleNum = limbTrigger.connect(ikHandle) poleNum = limbTrigger.connect(poleControl) fkIdx = limbTrigger.createMenu( "switch to FK", "zooAlign \"\";\nzooAlignFK \"-ikHandle %%%d -offCmd setAttr #.ikBlend 0;\";" % handleNum) limbTrigger.createMenu( "switch to FK for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\\"#\\\",(zooGetObjMenuCmdStr(\\\"#\\\",%%%d)),{}))";' % fkIdx) ikIdx = limbTrigger.createMenu( "switch to IK", 'zooAlign "";\nzooAlignIK "-ikHandle %%%d -pole %%%d -offCmd setAttr #.ikBlend 1;";' % (handleNum, poleNum)) limbTrigger.createMenu( "switch to IK for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\\"#\\\",(zooGetObjMenuCmdStr(\\\"#\\\",%%%d)),{}))";' % ikIdx) #add all zooObjMenu commands to the fk controls for fk in fkControls: fkTrigger = Trigger(fk) c1 = fkTrigger.connect(ikHandle) c2 = fkTrigger.connect(poleControl) #"zooFlags;\nzooAlign \"\";\nzooAlignIK \"-ikHandle %%%d -pole %%%d\";\nselect %%%d;" % (c1, c2, c1) ) fkTrigger.createMenu( 'switch to IK', 'zooAlign "";\nstring $cs[] = `listConnections %%%d.ikBlend`;\nzooAlignIK ("-ikHandle %%%d -pole %%%d -control "+ $cs[0] +" -offCmd setAttr "+ $cs[0] +".ikBlend 1;" );' % (c1, c1, c2)) createLineOfActionMenu([limbControl] + list(fkControls), (elbow, wrist)) #add trigger commands Trigger.CreateTrigger(lineNode, Trigger.PRESET_SELECT_CONNECTED, [poleControl]) setAttr('%s.displayHandle' % lineNode, True) #turn unwanted transforms off, so that they are locked, and no longer keyable attrState(fkControls, ('t', 'radi'), *LOCK_HIDE) attrState(poleControl, 'r', *LOCK_HIDE) return limbControl, driverUpper, driverMid, driverLower, poleControl, ikArmSpace, fkArmSpace, ikHandle, wristOrient, lineNode
def doBuild(self, bicep, elbow, wrist, nameScheme=ARM_NAMING_SCHEME, alignEnd=True, **kw): idx = kw["idx"] scale = kw["scale"] parity = Parity(idx) suffix = parity.asName() worldPart = WorldPart.Create() worldControl = worldPart.control partsControl = worldPart.parts colour = ColourDesc("green") if parity == Parity.LEFT else ColourDesc("red") # grab a list of 'bicep joints' - these are the child joints of the bicep that aren't the elbow or any of its # children. these joints are usually involved in deformation related to the bicep so we want to capture them # to use for geometry extraction for the control representation bicepJoints = [bicep] for child in listRelatives(bicep, pa=True, type="joint") or []: if cmpNodes(child, elbow): continue bicepJoints.append(child) bicepJoints += listRelatives(child, type="joint", pa=True, ad=True) or [] # grab the 'elbow joints' as per the above description elbowJoints = [elbow] for child in listRelatives(elbow, pa=True, type="joint") or []: if cmpNodes(child, wrist): continue elbowJoints.append(child) elbowJoints += listRelatives(child, type="joint", pa=True, ad=True) or [] # print 'THE BIPS', bicepJoints # print 'THE BOWS', elbowJoints ### BUILD THE FK CONTROLS ikArmSpace = buildAlignedNull(wrist, "ik_%sSpace%s" % (nameScheme[0], suffix), parent=worldControl) fkArmSpace = buildAlignedNull(bicep, "fk_%sSpace%s" % (nameScheme[0], suffix)) BONE_AXIS = AIM_AXIS + 3 if parity else AIM_AXIS driverUpper = buildControl( "fk_%sControl%s" % (nameScheme[1], suffix), bicep, PivotModeDesc.MID, Shape_Skin(bicepJoints, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, scale=scale, parent=fkArmSpace, ) driverMid = buildControl( "fk_%sControl%s" % (nameScheme[2], suffix), elbow, PivotModeDesc.MID, Shape_Skin(elbowJoints, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, scale=scale, parent=driverUpper, ) driverLower = buildControl( "fk_%sControl%s" % (nameScheme[3], suffix), PlaceDesc(wrist, wrist if alignEnd else None), shapeDesc=Shape_Skin(wrist, axis=BONE_AXIS), colour=colour, asJoint=True, oriented=False, constrain=False, scale=scale, ) # don't parent the driverLower in the buildControl command otherwise the control won't be in worldspace parent(driverLower, driverMid) makeIdentity(driverLower) ### BUILD THE POLE CONTROL polePos = rigUtils.findPolePosition(driverLower, driverMid, driverUpper, 5) poleControl = buildControl( "%s_poleControl%s" % (nameScheme[0], suffix), PlaceDesc(elbow, PlaceDesc.WORLD), shapeDesc=ShapeDesc("sphere", None), colour=colour, constrain=False, parent=worldControl, scale=scale * 0.5, ) poleControlSpace = getNodeParent(poleControl) attrState(poleControlSpace, "v", lock=False, show=True) move(polePos[0], polePos[1], polePos[2], poleControlSpace, a=True, ws=True, rpr=True) move(polePos[0], polePos[1], polePos[2], poleControl, a=True, ws=True, rpr=True) makeIdentity(poleControlSpace, a=True, t=True) setAttr("%s.v" % poleControl, False) ### BUILD THE POLE SELECTION TRIGGER lineNode = buildControl( "%s_poleSelectionTrigger%s" % (nameScheme[0], suffix), shapeDesc=ShapeDesc("sphere", None), colour=ColourDesc("darkblue"), scale=scale, constrain=False, oriented=False, parent=ikArmSpace, ) lineStart, lineEnd, lineShape = buildAnnotation(lineNode) parent(lineStart, poleControl) delete(pointConstraint(poleControl, lineStart)) pointConstraint(elbow, lineNode) attrState(lineNode, ("t", "r"), *LOCK_HIDE) setAttr("%s.template" % lineStart, 1) # make the actual line unselectable # build the IK handle ikHandle = cmd.ikHandle(fs=1, sj=driverUpper, ee=driverLower, solver="ikRPsolver")[0] limbControl = buildControl( "%sControl%s" % (nameScheme[0], suffix), PlaceDesc(wrist, wrist if alignEnd else None), shapeDesc=Shape_Skin(wrist, axis=BONE_AXIS), colour=colour, scale=scale, constrain=False, parent=ikArmSpace, ) xform(limbControl, p=True, rotateOrder="yzx") setAttr("%s.snapEnable" % ikHandle, False) setAttr("%s.v" % ikHandle, False) addAttr(limbControl, ln="ikBlend", shortName="ikb", dv=1, min=0, max=1, at="double") setAttr("%s.ikBlend" % limbControl, keyable=True) connectAttr("%s.ikBlend" % limbControl, "%s.ikBlend" % ikHandle) attrState(ikHandle, "v", *LOCK_HIDE) parent(ikHandle, partsControl) parentConstraint(limbControl, ikHandle) # parent( ikHandle, limbControl ) # poleVectorConstraint(poleControl, ikHandle) # setup constraints to the wrist - it is handled differently because it needs to blend between the ik and fk chains (the other controls are handled by maya) wristOrient = buildAlignedNull(wrist, "%s_follow%s" % (nameScheme[3], suffix), parent=partsControl) pointConstraint(driverLower, wrist) orientConstraint(wristOrient, wrist, mo=True) setItemRigControl(wrist, wristOrient) wristSpaceOrient = parentConstraint(limbControl, wristOrient, weight=0, mo=True)[0] wristSpaceOrient = parentConstraint(driverLower, wristOrient, weight=0, mo=True)[0] setAttr("%s.interpType" % wristSpaceOrient, 2) # connect the ikBlend of the arm controller to the orient constraint of the fk wrist - ie turn it off when ik is off... weightRevNode = shadingNode("reverse", asUtility=True) wristOrientAttrs = listAttr(wristSpaceOrient, ud=True) connectAttr("%s.ikBlend" % limbControl, "%s.inputX" % weightRevNode, f=True) connectAttr("%s.ikBlend" % limbControl, "%s.%s" % (wristSpaceOrient, wristOrientAttrs[0]), f=True) connectAttr("%s.outputX" % weightRevNode, "%s.%s" % (wristSpaceOrient, wristOrientAttrs[1]), f=True) # build expressions for fk blending and control visibility fkVisCond = shadingNode("condition", asUtility=True) poleVisCond = shadingNode("condition", asUtility=True) connectAttr("%s.ikBlend" % limbControl, "%s.firstTerm" % fkVisCond, f=True) connectAttr("%s.ikBlend" % limbControl, "%s.firstTerm" % poleVisCond, f=True) connectAttr("%s.outColorR" % fkVisCond, "%s.v" % driverUpper, f=True) connectAttr("%s.outColorG" % poleVisCond, "%s.v" % poleControlSpace, f=True) connectAttr("%s.outColorG" % poleVisCond, "%s.v" % limbControl, f=True) setAttr("%s.secondTerm" % fkVisCond, 1) # add set pole to fk pos command to pole control fkControls = driverUpper, driverMid, driverLower poleTrigger = Trigger(poleControl) poleConnectNums = [poleTrigger.connect(c) for c in fkControls] idx_toFK = poleTrigger.setMenuInfo( None, "move to FK position", 'zooVectors;\nfloat $pos[] = `zooFindPolePosition "-start %%%s -mid %%%s -end %%%s"`;\nmove -rpr $pos[0] $pos[1] $pos[2] #;' % tuple(poleConnectNums), ) poleTrigger.setMenuInfo( None, "move to FK pos for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\"#\\",(zooGetObjMenuCmdStr(\\"#\\",%%%d)),{}))";' % idx_toFK, ) ##build the post trace commands for the pole vectors - once they've been placed after a trace, its safe and almost always ##desireable to place the pole vectors a little more sensibly # zooSetPostTraceCmd $poleControl ( "zooVectors; zooPlacePole \"-obj # -start %"+ $poleConnectNums[0] +" -mid %"+ $poleConnectNums[1] +" -end %"+ $poleConnectNums[2] +" -key 1 -removeKey 1 -invalidMode 1\";" ); # add IK/FK switching commands limbTrigger = Trigger(limbControl) handleNum = limbTrigger.connect(ikHandle) poleNum = limbTrigger.connect(poleControl) fkIdx = limbTrigger.createMenu( "switch to FK", 'zooAlign "";\nzooAlignFK "-ikHandle %%%d -offCmd setAttr #.ikBlend 0;";' % handleNum ) limbTrigger.createMenu( "switch to FK for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\"#\\",(zooGetObjMenuCmdStr(\\"#\\",%%%d)),{}))";' % fkIdx, ) ikIdx = limbTrigger.createMenu( "switch to IK", 'zooAlign "";\nzooAlignIK "-ikHandle %%%d -pole %%%d -offCmd setAttr #.ikBlend 1;";' % (handleNum, poleNum), ) limbTrigger.createMenu( "switch to IK for all keys", 'source zooKeyCommandsWin;\nzooSetKeyCommandsWindowCmd "eval(zooPopulateCmdStr(\\"#\\",(zooGetObjMenuCmdStr(\\"#\\",%%%d)),{}))";' % ikIdx, ) # add all zooObjMenu commands to the fk controls for fk in fkControls: fkTrigger = Trigger(fk) c1 = fkTrigger.connect(ikHandle) c2 = fkTrigger.connect(poleControl) # "zooFlags;\nzooAlign \"\";\nzooAlignIK \"-ikHandle %%%d -pole %%%d\";\nselect %%%d;" % (c1, c2, c1) ) fkTrigger.createMenu( "switch to IK", 'zooAlign "";\nstring $cs[] = `listConnections %%%d.ikBlend`;\nzooAlignIK ("-ikHandle %%%d -pole %%%d -control "+ $cs[0] +" -offCmd setAttr "+ $cs[0] +".ikBlend 1;" );' % (c1, c1, c2), ) createLineOfActionMenu([limbControl] + list(fkControls), (elbow, wrist)) # add trigger commands Trigger.CreateTrigger(lineNode, Trigger.PRESET_SELECT_CONNECTED, [poleControl]) setAttr("%s.displayHandle" % lineNode, True) # turn unwanted transforms off, so that they are locked, and no longer keyable attrState(fkControls, ("t", "radi"), *LOCK_HIDE) attrState(poleControl, "r", *LOCK_HIDE) return ( limbControl, driverUpper, driverMid, driverLower, poleControl, ikArmSpace, fkArmSpace, ikHandle, wristOrient, lineNode, )