def createJntTweak(mesh, jntParent, ctlParent): """Create a joint tweak Args: mesh (mesh): The object to deform with the tweak jntParent (dagNode): The parent for the new joint ctlParent (dagNode): The parent for the control. """ if not isinstance(mesh, list): mesh = [mesh] name = "_".join(jntParent.name().split("_")[:3]) # create joints jointBase = primitive.addJoint(jntParent, name + "_tweak_jnt_lvl", jntParent.getMatrix(worldSpace=True)) resetJntLocalSRT(jointBase) joint = primitive.addJoint(jointBase, name + "_tweak_jnt", jntParent.getMatrix(worldSpace=True)) resetJntLocalSRT(joint) # hiding joint base by changing the draw mode # pm.setAttr(jointBase+".drawStyle", 2) try: defSet = pm.PyNode("rig_deformers_grp") except TypeError: pm.sets(n="rig_deformers_grp") defSet = pm.PyNode("rig_deformers_grp") pm.sets(defSet, add=joint) controlType = "circle" iconBase = icon.create(ctlParent, name + "_base_tweak_ctl", ctlParent.getMatrix(worldSpace=True), 13, controlType, w=.8, ro=datatypes.Vector(0, 0, 1.5708)) attribute.addAttribute(iconBase, "isCtl", "bool", keyable=False) o_icon = icon.create(iconBase, name + "_tweak_ctl", ctlParent.getMatrix(worldSpace=True), 17, controlType, w=.5, ro=datatypes.Vector(0, 0, 1.5708)) attribute.addAttribute(o_icon, "isCtl", "bool", keyable=False) for t in [".translate", ".scale", ".rotate"]: pm.connectAttr(iconBase + t, jointBase + t) pm.connectAttr(o_icon + t, joint + t) # magic of doritos connection for m in mesh: pre_bind_matrix_connect(m, joint, jointBase)
def addJointTransformFromPos(parent, name, pos=datatypes.Vector(0, 0, 0)): """Create a transform dagNode. Arguments: parent (dagNode): The parent for the node. name (str): The Node name. pos (vector): The vector for the node position (optional). Returns: dagNode: The newly created node. """ node = primitive.addJoint(parent, name) node.setAttr("drawStyle", 2) # draw none for shape in node.getShapes(): pm.delete(shape) node.setTranslation(pos, space="world") return node
def addJointTransform(parent, name, m=datatypes.Matrix()): """Create a transform dagNode. Arguments: parent (dagNode): The parent for the node. name (str): The Node name. m (matrix): The matrix for the node transformation (optional). Returns: dagNode: The newly created node. """ # node = pm.PyNode(pm.createNode("transform", n=name)) # node.setTransformation(m) node = primitive.addJoint(parent, name, m) node.setAttr("drawStyle", 2) # draw none for shape in node.getShapes(): pm.delete(shape) return node
def createRivetTweakLayer(layerMesh, bst, edgePairList, name, parent=None, ctlParent=None, jntParent=None, color=[0, 0, 0], size=.04, defSet=None, ctlSet=None, side=None, mirror=False, mParent=None, mCtlParent=None, mjntParent=None, mColor=None, gearMulMatrix=True, static_jnt=None, attach_rot=False, inputMesh=None): """Create a rivet tweak layer setup Args: layerMesh (mesh): The tweak layer mesh bst (mesh): The mesh blendshape target edgePairList (list of list): The edge pair list of list name (str): The name for the tweak parent (None or dagNode, optional): The parent for the tweak jntParent (None or dagNode, optional): The parent for the joints ctlParent (None or dagNode, optional): The parent for the tweak control color (list, optional): The color for the control size (float, optional): Size of the control defSet (None or set, optional): Deformer set to add the joints ctlSet (None or set, optional): the set to add the controls side (None, str): String to set the side. Valid values are L, R or C. If the side is not set or the value is not valid, the side will be set automatically based on the world position mirror (bool, optional): Create the mirror tweak on X axis symmetry mParent (None, optional): Mirror tweak parent, if None will use parent arg mjntParent (None, optional): Mirror parent joint, if None will use jntParent arg mCtlParent (None, optional): Mirror ctl parent, if None will use ctlParent arg mColor (None, optional): Mirror controls color, if None will color arg gearMulMatrix (bool, optional): If False will use Maya default multiply matrix node static_jnt (dagNode, optional): Static joint for the setup """ # Apply blendshape from blendshapes layer mesh blendShapes.connectWithBlendshape(layerMesh, bst) # create static joint if not static_jnt: if pm.objExists('static_jnt') is not True: static_jnt = primitive.addJoint(parent, "static_jnt", m=datatypes.Matrix(), vis=True) else: static_jnt = pm.PyNode("static_jnt") # apply initial skincluster pm.skinCluster(static_jnt, layerMesh, tsb=True, nw=2, n='%s_skinCluster' % layerMesh.name()) # create doritos if not mParent: mParent = parent if not mCtlParent: mCtlParent = ctlParent if not mColor: mColor = color if not mjntParent: mjntParent = jntParent createRivetTweakFromList(layerMesh, edgePairList, name, parent=parent, ctlParent=ctlParent, jntParent=jntParent, color=color, size=size, defSet=defSet, ctlSet=ctlSet, side=side, mirror=mirror, mParent=mParent, mCtlParent=mCtlParent, mjntParent=mjntParent, mColor=mColor, gearMulMatrix=gearMulMatrix, attach_rot=attach_rot, inputMesh=inputMesh)
def createRivetTweak(mesh, edgePair, name, parent=None, ctlParent=None, jntParent=None, color=[0, 0, 0], size=.04, defSet=None, ctlSet=None, side=None, gearMulMatrix=True, attach_rot=False, inputMesh=None): """Create a tweak joint attached to the mesh using a rivet Args: mesh (mesh): The object to add the tweak edgePair (pair list): The edge pair to create the rivet name (str): The name for the tweak parent (None or dagNode, optional): The parent for the tweak jntParent (None or dagNode, optional): The parent for the joints ctlParent (None or dagNode, optional): The parent for the tweak control color (list, optional): The color for the control size (float, optional): Size of the control defSet (None or set, optional): Deformer set to add the joints ctlSet (None or set, optional): the set to add the controls side (None, str): String to set the side. Valid values are L, R or C. If the side is not set or the value is not valid, the side will be set automatically based on the world position gearMulMatrix (bool, optional): If False will use Maya default multiply matrix node Returns: PyNode: The tweak control """ blendShape = blendShapes.getBlendShape(mesh) if not inputMesh: inputMesh = blendShape.listConnections(sh=True, t="shape", d=False)[0] oRivet = rivet.rivet() base = oRivet.create(inputMesh, edgePair[0], edgePair[1], parent) # get side if not side or side not in ["L", "R", "C"]: if base.getTranslation(space='world')[0] < -0.01: side = "R" elif base.getTranslation(space='world')[0] > 0.01: side = "L" else: side = "C" nameSide = name + "_tweak_" + side pm.rename(base, nameSide) if not ctlParent: ctlParent = base ctl_parent_tag = None else: ctl_parent_tag = ctlParent # Joints NPO npo = pm.PyNode(pm.createNode("transform", n=nameSide + "_npo", p=ctlParent, ss=True)) if attach_rot: # npo.setTranslation(base.getTranslation(space="world"), space="world") pm.parentConstraint(base, npo, mo=False) else: pm.pointConstraint(base, npo, mo=False) # create joints if not jntParent: jntParent = npo matrix_cnx = False else: # need extra connection to ensure is moving with th npo, even is # not child of npo matrix_cnx = True jointBase = primitive.addJoint(jntParent, nameSide + "_jnt_lvl") joint = primitive.addJoint(jointBase, nameSide + "_jnt") # reset axis and invert behaviour for axis in "XYZ": pm.setAttr(jointBase + ".jointOrient" + axis, 0) pm.setAttr(npo + ".translate" + axis, 0) # pm.setAttr(jointBase + ".translate" + axis, 0) pp = npo.getParent() pm.parent(npo, w=True) for axis in "xyz": npo.attr("r" + axis).set(0) if side == "R": npo.attr("ry").set(180) npo.attr("sz").set(-1) pm.parent(npo, pp) dm_node = None if matrix_cnx: mulmat_node = applyop.gear_mulmatrix_op( npo + ".worldMatrix", jointBase + ".parentInverseMatrix") dm_node = node.createDecomposeMatrixNode( mulmat_node + ".output") m = mulmat_node.attr('output').get() pm.connectAttr(dm_node + ".outputTranslate", jointBase + ".t") pm.connectAttr(dm_node + ".outputRotate", jointBase + ".r") # invert negative scaling in Joints. We only inver Z axis, so is # the only axis that we are checking print(dm_node.attr("outputScaleZ").get()) if dm_node.attr("outputScaleZ").get() < 0: mul_nod_invert = node.createMulNode( dm_node.attr("outputScaleZ"), -1) out_val = mul_nod_invert.attr("outputX") else: out_val = dm_node.attr("outputScaleZ") pm.connectAttr(dm_node.attr("outputScaleX"), jointBase + ".sx") pm.connectAttr(dm_node.attr("outputScaleY"), jointBase + ".sy") pm.connectAttr(out_val, jointBase + ".sz") pm.connectAttr(dm_node + ".outputShear", jointBase + ".shear") # Segment scale compensate Off to avoid issues with the global # scale jointBase.setAttr("segmentScaleCompensate", 0) joint.setAttr("segmentScaleCompensate", 0) jointBase.setAttr("jointOrient", 0, 0, 0) # setting the joint orient compensation in order to have clean # rotation channels jointBase.attr("jointOrientX").set(jointBase.attr("rx").get()) jointBase.attr("jointOrientY").set(jointBase.attr("ry").get()) jointBase.attr("jointOrientZ").set(jointBase.attr("rz").get()) im = m.inverse() if gearMulMatrix: mul_nod = applyop.gear_mulmatrix_op( mulmat_node.attr('output'), im, jointBase, 'r') dm_node2 = mul_nod.output.listConnections()[0] else: mul_nod = node.createMultMatrixNode( mulmat_node.attr('matrixSum'), im, jointBase, 'r') dm_node2 = mul_nod.matrixSum.listConnections()[0] if dm_node.attr("outputScaleZ").get() < 0: negateTransformConnection(dm_node2.outputRotate, jointBase.rotate) else: resetJntLocalSRT(jointBase) # hidding joint base by changing the draw mode pm.setAttr(jointBase + ".drawStyle", 2) if not defSet: try: defSet = pm.PyNode("rig_deformers_grp") except TypeError: pm.sets(n="rig_deformers_grp", empty=True) defSet = pm.PyNode("rig_deformers_grp") pm.sets(defSet, add=joint) controlType = "sphere" o_icon = icon.create(npo, nameSide + "_ctl", datatypes.Matrix(), color, controlType, w=size) attribute.addAttribute(o_icon, "isCtl", "bool", keyable=False) transform.resetTransform(o_icon) if dm_node and dm_node.attr("outputScaleZ").get() < 0: pm.connectAttr(o_icon.scale, joint.scale) negateTransformConnection(o_icon.rotate, joint.rotate) negateTransformConnection(o_icon.translate, joint.translate, [1, 1, -1]) else: for t in [".translate", ".scale", ".rotate"]: pm.connectAttr(o_icon + t, joint + t) # create the attributes to handlde mirror and symetrical pose attribute.addAttribute( o_icon, "invTx", "bool", 0, keyable=False, niceName="Invert Mirror TX") attribute.addAttribute( o_icon, "invTy", "bool", 0, keyable=False, niceName="Invert Mirror TY") attribute.addAttribute( o_icon, "invTz", "bool", 0, keyable=False, niceName="Invert Mirror TZ") attribute.addAttribute( o_icon, "invRx", "bool", 0, keyable=False, niceName="Invert Mirror RX") attribute.addAttribute( o_icon, "invRy", "bool", 0, keyable=False, niceName="Invert Mirror RY") attribute.addAttribute( o_icon, "invRz", "bool", 0, keyable=False, niceName="Invert Mirror RZ") attribute.addAttribute( o_icon, "invSx", "bool", 0, keyable=False, niceName="Invert Mirror SX") attribute.addAttribute( o_icon, "invSy", "bool", 0, keyable=False, niceName="Invert Mirror SY") attribute.addAttribute( o_icon, "invSz", "bool", 0, keyable=False, niceName="Invert Mirror SZ") # magic of doritos connection pre_bind_matrix_connect(mesh, joint, jointBase) # add control tag node.add_controller_tag(o_icon, ctl_parent_tag) if not ctlSet: try: ctlSet = pm.PyNode("rig_controllers_grp") except TypeError: pm.sets(n="rig_controllers_grp", empty=True) ctlSet = pm.PyNode("rig_controllers_grp") pm.sets(ctlSet, add=o_icon) return o_icon
def addJoint(self, obj, name, newActiveJnt=None, UniScale=False, segComp=0, gearMulMatrix=True): """Add joint as child of the active joint or under driver object. Args: obj (dagNode): The input driver object for the joint. name (str): The joint name. newActiveJnt (bool or dagNode): If a joint is pass, this joint will be the active joint and parent of the newly created joint. UniScale (bool): Connects the joint scale with the Z axis for a unifor scalin, if set Falsewill connect with each axis separated. segComp (bool): Set True or False the segment compensation in the joint.. gearMulMatrix (bool): Use the custom gear_multiply matrix node, if False will use Maya's default mulMatrix node. Returns: dagNode: The newly created joint. """ customName = self.getCustomJointName(len(self.jointList)) if self.options["joint_rig"]: if newActiveJnt: self.active_jnt = newActiveJnt jnt = primitive.addJoint(self.active_jnt, customName or self.getName(str(name) + "_jnt"), transform.getTransform(obj)) # Disconnect inversScale for better preformance if isinstance(self.active_jnt, pm.nodetypes.Joint): try: pm.disconnectAttr(self.active_jnt.scale, jnt.inverseScale) except RuntimeError: # This handle the situation where we have in between joints # transformation due a negative scaling pm.ungroup(jnt.getParent()) # All new jnts are the active by default self.active_jnt = jnt if gearMulMatrix: mulmat_node = applyop.gear_mulmatrix_op( obj + ".worldMatrix", jnt + ".parentInverseMatrix") dm_node = node.createDecomposeMatrixNode( mulmat_node + ".output") m = mulmat_node.attr('output').get() else: mulmat_node = node.createMultMatrixNode( obj + ".worldMatrix", jnt + ".parentInverseMatrix") dm_node = node.createDecomposeMatrixNode( mulmat_node + ".matrixSum") m = mulmat_node.attr('matrixSum').get() pm.connectAttr(dm_node + ".outputTranslate", jnt + ".t") pm.connectAttr(dm_node + ".outputRotate", jnt + ".r") # TODO: fix squash stretch solver to scale the joint uniform # the next line cheat the uniform scaling only fo X or Y axis # oriented joints if self.options["force_uniScale"]: UniScale = True # invert negative scaling in Joints. We only inver Z axis, so is # the only axis that we are checking if dm_node.attr("outputScaleZ").get() < 0: mul_nod_invert = node.createMulNode( dm_node.attr("outputScaleZ"), -1) out_val = mul_nod_invert.attr("outputX") else: out_val = dm_node.attr("outputScaleZ") # connect scaling if UniScale: pm.connectAttr(out_val, jnt + ".sx") pm.connectAttr(out_val, jnt + ".sy") pm.connectAttr(out_val, jnt + ".sz") else: pm.connectAttr(dm_node.attr("outputScaleX"), jnt + ".sx") pm.connectAttr(dm_node.attr("outputScaleY"), jnt + ".sy") pm.connectAttr(out_val, jnt + ".sz") pm.connectAttr(dm_node + ".outputShear", jnt + ".shear") # Segment scale compensate Off to avoid issues with the global # scale jnt.setAttr("segmentScaleCompensate", segComp) jnt.setAttr("jointOrient", 0, 0, 0) # setting the joint orient compensation in order to have clean # rotation channels jnt.attr("jointOrientX").set(jnt.attr("rx").get()) jnt.attr("jointOrientY").set(jnt.attr("ry").get()) jnt.attr("jointOrientZ").set(jnt.attr("rz").get()) im = m.inverse() if gearMulMatrix: mul_nod = applyop.gear_mulmatrix_op( mulmat_node.attr('output'), im, jnt, 'r') dm_node2 = mul_nod.output.listConnections()[0] else: mul_nod = node.createMultMatrixNode( mulmat_node.attr('matrixSum'), im, jnt, 'r') dm_node2 = mul_nod.matrixSum.listConnections()[0] # if jnt.attr("sz").get() < 0: if dm_node.attr("outputScaleZ").get() < 0: # if negative scaling we have to negate some axis for rotation neg_rot_node = pm.createNode("multiplyDivide") pm.setAttr(neg_rot_node + ".operation", 1) pm.connectAttr(dm_node2.outputRotate, neg_rot_node + ".input1", f=True) for v, axis in zip([-1, -1, 1], "XYZ"): pm.setAttr(neg_rot_node + ".input2" + axis, v) pm.connectAttr(neg_rot_node + ".output", jnt + ".r", f=True) # set not keyable attribute.setNotKeyableAttributes(jnt) else: jnt = primitive.addJoint(obj, customName or self.getName(str(name) + "_jnt"), transform.getTransform(obj)) pm.connectAttr(self.rig.jntVis_att, jnt.attr("visibility")) attribute.lockAttribute(jnt) self.addToGroup(jnt, "deformers") # This is a workaround due the evaluation problem with compound attr # TODO: This workaround, should be removed onces the evaluation issue # is fixed # github issue: Shifter: Joint connection: Maya evaluation Bug #210 dm = jnt.r.listConnections(p=True, type="decomposeMatrix") if dm: at = dm[0] dm_node = at.node() pm.disconnectAttr(at, jnt.r) pm.connectAttr(dm_node.outputRotateX, jnt.rx) pm.connectAttr(dm_node.outputRotateY, jnt.ry) pm.connectAttr(dm_node.outputRotateZ, jnt.rz) dm = jnt.t.listConnections(p=True, type="decomposeMatrix") if dm: at = dm[0] dm_node = at.node() pm.disconnectAttr(at, jnt.t) pm.connectAttr(dm_node.outputTranslateX, jnt.tx) pm.connectAttr(dm_node.outputTranslateY, jnt.ty) pm.connectAttr(dm_node.outputTranslateZ, jnt.tz) # dm = jnt.s.listConnections(p=True, type="decomposeMatrix") # if dm: # at = dm[0] # dm_node = at.node() # pm.disconnectAttr(at, jnt.s) # pm.connectAttr(dm_node.outputScaleX, jnt.sx) # pm.connectAttr(dm_node.outputScaleY, jnt.sy) # pm.connectAttr(dm_node.outputScaleZ, jnt.sz) return jnt
def addObjects(self): """Add all the objects needed to create the component.""" self.setup = primitive.addTransformFromPos( self.setupWS, self.getName("WS")) attribute.lockAttribute(self.setup) self.WIP = self.options["mode"] self.normal = self.getNormalFromPos(self.guide.apos) self.length0 = vector.getDistance(self.guide.apos[0], self.guide.apos[1]) self.length1 = vector.getDistance(self.guide.apos[1], self.guide.apos[2]) self.length2 = vector.getDistance(self.guide.apos[2], self.guide.apos[3]) self.length3 = vector.getDistance(self.guide.apos[3], self.guide.apos[4]) # 3bones chain self.chain3bones = primitive.add2DChain( self.setup, self.getName("chain3bones%s_jnt"), self.guide.apos[0:4], self.normal, False, self.WIP) # 2bones chain self.chain2bones = primitive.add2DChain( self.setup, self.getName("chain2bones%s_jnt"), self.guide.apos[0:3], self.normal, False, self.WIP) # Leg chain self.legBones = primitive.add2DChain( self.root, self.getName("legBones%s_jnt"), self.guide.apos[0:4], self.normal, False, self.WIP) # Leg chain FK ref self.legBonesFK = primitive.add2DChain( self.root, self.getName("legFK%s_jnt"), self.guide.apos[0:4], self.normal, False, self.WIP) # Leg chain IK ref self.legBonesIK = primitive.add2DChain( self.root, self.getName("legIK%s_jnt"), self.guide.apos[0:4], self.normal, False, self.WIP) # 1 bone chain for upv ref self.legChainUpvRef = primitive.add2DChain( self.root, self.getName("legUpvRef%s_jnt"), [self.guide.apos[0], self.guide.apos[3]], self.normal, False, self.WIP) # mid joints self.mid1_jnt = primitive.addJoint( self.legBones[0], self.getName("mid1_jnt"), self.legBones[1].getMatrix(worldSpace=True), self.WIP) self.mid1_jnt.attr("radius").set(3) self.mid1_jnt.setAttr("jointOrient", 0, 0, 0) self.mid2_jnt = primitive.addJoint( self.legBones[1], self.getName("mid2_jnt"), self.legBones[2].getMatrix(worldSpace=True), self.WIP) self.mid2_jnt.attr("radius").set(3) self.mid2_jnt.setAttr("jointOrient", 0, 0, 0) # base Controlers ----------------------------------- t = transform.getTransformFromPos(self.guide.apos[0]) self.root_npo = primitive.addTransform( self.root, self.getName("root_npo"), t) self.root_ctl = self.addCtl(self.root_npo, "root_ctl", t, self.color_fk, "circle", w=self.length0 / 6, tp=self.parentCtlTag) attribute.lockAttribute(self.root_ctl, ["sx", "sy", "sz", "v"]) # FK Controlers ----------------------------------- t = transform.getTransformLookingAt(self.guide.apos[0], self.guide.apos[1], self.normal, "xz", self.negate) self.fk0_npo = primitive.addTransform( self.root_ctl, self.getName("fk0_npo"), t) self.fk0_ctl = self.addCtl( self.fk0_npo, "fk0_ctl", t, self.color_fk, "cube", w=self.length0, h=self.size * .1, d=self.size * .1, po=datatypes.Vector(.5 * self.length0 * self.n_factor, 0, 0), tp=self.root_ctl) attribute.setKeyableAttributes(self.fk0_ctl) t = transform.getTransformLookingAt(self.guide.apos[1], self.guide.apos[2], self.normal, "xz", self.negate) self.fk1_npo = primitive.addTransform( self.fk0_ctl, self.getName("fk1_npo"), t) self.fk1_ctl = self.addCtl( self.fk1_npo, "fk1_ctl", t, self.color_fk, "cube", w=self.length1, h=self.size * .1, d=self.size * .1, po=datatypes.Vector(.5 * self.length1 * self.n_factor, 0, 0), tp=self.fk0_ctl) attribute.setKeyableAttributes(self.fk1_ctl) t = transform.getTransformLookingAt(self.guide.apos[2], self.guide.apos[3], self.normal, "xz", self.negate) self.fk2_npo = primitive.addTransform( self.fk1_ctl, self.getName("fk2_npo"), t) self.fk2_ctl = self.addCtl( self.fk2_npo, "fk2_ctl", t, self.color_fk, "cube", w=self.length2, h=self.size * .1, d=self.size * .1, po=datatypes.Vector(.5 * self.length2 * self.n_factor, 0, 0), tp=self.fk1_ctl) attribute.setKeyableAttributes(self.fk2_ctl) t = transform.getTransformLookingAt(self.guide.apos[3], self.guide.apos[4], self.normal, "xz", self.negate) self.fk3_npo = primitive.addTransform( self.fk2_ctl, self.getName("fk3_npo"), t) self.fk3_ctl = self.addCtl( self.fk3_npo, "fk3_ctl", t, self.color_fk, "cube", w=self.length3, h=self.size * .1, d=self.size * .1, po=datatypes.Vector(.5 * self.length3 * self.n_factor, 0, 0), tp=self.fk2_ctl) attribute.setKeyableAttributes(self.fk3_ctl) self.fk_ctl = [self.fk0_ctl, self.fk1_ctl, self.fk2_ctl, self.fk3_ctl] for x in self.fk_ctl: attribute.setInvertMirror(x, ["tx", "ty", "tz"]) # Mid Controlers ------------------------------------ self.knee_lvl = primitive.addTransform( self.root, self.getName("knee_lvl"), transform.getTransform(self.mid1_jnt)) self.knee_ctl = self.addCtl( self.knee_lvl, "knee_ctl", transform.getTransform(self.mid1_jnt), self.color_ik, "sphere", w=self.size * .2, tp=self.root_ctl) attribute.setInvertMirror(self.knee_ctl, ["tx", "ty", "tz"]) attribute.lockAttribute(self.knee_ctl, ["sx", "sy", "sz", "v"]) self.ankle_lvl = primitive.addTransform( self.root, self.getName("ankle_lvl"), transform.getTransform(self.mid2_jnt)) self.ankle_ctl = self.addCtl( self.ankle_lvl, "ankle_ctl", transform.getTransform(self.mid2_jnt), self.color_ik, "sphere", w=self.size * .2, tp=self.knee_ctl) attribute.setInvertMirror(self.ankle_ctl, ["tx", "ty", "tz"]) attribute.lockAttribute(self.ankle_ctl, ["sx", "sy", "sz", "v"]) # IK controls -------------------------------------------------------- # foot IK # "z-x", t_align = transform.getTransformLookingAt(self.guide.apos[3], self.guide.apos[4], self.normal, "zx", False) if self.settings["ikOri"]: t = transform.getTransformFromPos(self.guide.pos["foot"]) # t = transform.getTransformLookingAt(self.guide.pos["foot"], # self.guide.pos["eff"], # self.x_axis, # "zx", # False) else: t = t_align self.ik_cns = primitive.addTransform( self.root_ctl, self.getName("ik_cns"), t) self.ikcns_ctl = self.addCtl(self.ik_cns, "ikcns_ctl", t, self.color_ik, "null", w=self.size * .12, tp=self.ankle_ctl) attribute.setInvertMirror(self.ikcns_ctl, ["tx"]) attribute.lockAttribute(self.ikcns_ctl, ["sx", "sy", "sz", "v"]) self.ik_ctl = self.addCtl(self.ikcns_ctl, "ik_ctl", t, self.color_ik, "cube", w=self.size * .12, h=self.size * .12, d=self.size * .12, tp=self.ikcns_ctl) attribute.setKeyableAttributes(self.ik_ctl) attribute.setRotOrder(self.ik_ctl, "XZY") attribute.setInvertMirror(self.ik_ctl, ["tx", "ry", "rz"]) attribute.lockAttribute(self.ik_ctl, ["sx", "sy", "sz", "v"]) # 2 bones ik layer self.ik2b_ikCtl_ref = primitive.addTransform( self.ik_ctl, self.getName("ik2B_A_ref"), t_align) self.ik2b_bone_ref = primitive.addTransform( self.chain3bones[3], self.getName("ik2B_B_ref"), t_align) self.ik2b_blend = primitive.addTransform( self.ik_ctl, self.getName("ik2B_blend"), t_align) self.roll_ctl = self.addCtl(self.ik2b_blend, "roll_ctl", t_align, self.color_ik, "crossarrow", w=self.length2 * .5 * self.n_factor, tp=self.ik_ctl) self.ik2b_ik_npo = primitive.addTransform( self.roll_ctl, self.getName("ik2B_ik_npo"), transform.getTransform(self.chain3bones[-1])) self.ik2b_ik_ref = primitive.addTransformFromPos( self.ik2b_ik_npo, self.getName("ik2B_ik_ref"), self.guide.pos["ankle"]) attribute.lockAttribute( self.roll_ctl, ["tx", "ty", "tz", "sx", "sy", "sz", "v"]) # upv v = self.guide.apos[2] - self.guide.apos[0] v = self.normal ^ v v.normalize() v *= self.size * .5 v += self.guide.apos[1] self.upv_lvl = primitive.addTransformFromPos( self.root, self.getName("upv_lvl"), v) self.upv_cns = primitive.addTransformFromPos( self.upv_lvl, self.getName("upv_cns"), v) self.upv_ctl = self.addCtl(self.upv_cns, "upv_ctl", transform.getTransform(self.upv_cns), self.color_ik, "diamond", w=self.size * .12, tp=self.ik_ctl) attribute.setInvertMirror(self.upv_ctl, ["tx"]) attribute.setKeyableAttributes(self.upv_ctl, ["tx", "ty", "tz"]) # Soft IK objects 3 bones chain -------------------------------- t = transform.getTransformLookingAt(self.guide.pos["root"], self.guide.pos["foot"], self.x_axis, "zx", False) self.aim_tra = primitive.addTransform( self.root_ctl, self.getName("aimSoftIK"), t) t = transform.getTransformFromPos(self.guide.pos["foot"]) self.wristSoftIK = primitive.addTransform( self.aim_tra, self.getName("wristSoftIK"), t) self.softblendLoc = primitive.addTransform( self.root, self.getName("softblendLoc"), t) # Soft IK objects 2 Bones chain ---------------------------- t = transform.getTransformLookingAt(self.guide.pos["root"], self.guide.pos["ankle"], self.x_axis, "zx", False) self.aim_tra2 = primitive.addTransform( self.root_ctl, self.getName("aimSoftIK2"), t) t = transform.getTransformFromPos(self.guide.pos["ankle"]) self.ankleSoftIK = primitive.addTransform( self.aim_tra2, self.getName("ankleSoftIK"), t) self.softblendLoc2 = primitive.addTransform( self.root, self.getName("softblendLoc2"), t) # References -------------------------------------- self.ik_ref = primitive.addTransform( self.ik_ctl, self.getName("ik_ref"), transform.getTransform(self.ik_ctl)) self.fk_ref = primitive.addTransform( self.fk_ctl[3], self.getName("fk_ref"), transform.getTransform(self.ik_ctl)) # twist references -------------------------------------- self.rollRef = primitive.add2DChain(self.root, self.getName("rollChain"), self.guide.apos[:2], self.normal, False, self.WIP) self.tws0_loc = primitive.addTransform( self.rollRef[0], self.getName("tws0_loc"), transform.getTransform(self.legBones[0])) self.tws0_rot = primitive.addTransform( self.tws0_loc, self.getName("tws0_rot"), transform.getTransform(self.legBones[0])) self.tws0_rot.setAttr("sx", .001) self.tws1_loc = primitive.addTransform( self.mid1_jnt, self.getName("tws1_loc"), transform.getTransform(self.mid1_jnt)) self.tws1_rot = primitive.addTransform( self.tws1_loc, self.getName("tws1_rot"), transform.getTransform(self.mid1_jnt)) self.tws1_rot.setAttr("sx", .001) self.tws2_loc = primitive.addTransform( self.mid2_jnt, self.getName("tws2_loc"), transform.getTransform(self.mid2_jnt)) self.tws2_rot = primitive.addTransform( self.tws2_loc, self.getName("tws2_rot"), transform.getTransform(self.mid2_jnt)) self.tws2_rot.setAttr("sx", .001) self.tws3_loc = primitive.addTransform( self.legBones[3], self.getName("tws3_loc"), transform.getTransform(self.legBones[3])) self.tws3_rot = primitive.addTransform( self.tws3_loc, self.getName("tws3_rot"), transform.getTransform(self.legBones[3])) self.tws3_rot.setAttr("sx", .001) # Divisions ---------------------------------------- # We have at least one division at the start, the end and one for # the knee and one ankle o_set = self.settings self.divisions = o_set["div0"] + o_set["div1"] + o_set["div2"] + 4 self.div_cns = [] for i in range(self.divisions): div_cns = primitive.addTransform(self.root_ctl, self.getName("div%s_loc" % i)) self.div_cns.append(div_cns) self.jnt_pos.append([div_cns, i]) # End reference ------------------------------------ # To help the deformation on the foot self.end_ref = primitive.addTransform( self.tws3_rot, self.getName("end_ref"), transform.getTransform(self.legBones[3])) self.jnt_pos.append([self.end_ref, 'end']) # match IK FK references self.match_fk0_off = primitive.addTransform( self.root, self.getName("matchFk0_npo"), transform.getTransform(self.fk_ctl[1])) self.match_fk0 = primitive.addTransform( self.match_fk0_off, self.getName("fk0_mth"), transform.getTransform(self.fk_ctl[0])) self.match_fk1_off = primitive.addTransform( self.root, self.getName("matchFk1_npo"), transform.getTransform(self.fk_ctl[2])) self.match_fk1 = primitive.addTransform( self.match_fk1_off, self.getName("fk1_mth"), transform.getTransform(self.fk_ctl[1])) self.match_fk2_off = primitive.addTransform( self.root, self.getName("matchFk2_npo"), transform.getTransform(self.fk_ctl[3])) self.match_fk2 = primitive.addTransform( self.match_fk2_off, self.getName("fk2_mth"), transform.getTransform(self.fk_ctl[2])) self.match_fk3 = primitive.addTransform( self.ik_ctl, self.getName("fk3_mth"), transform.getTransform(self.fk_ctl[3])) self.match_ik = primitive.addTransform( self.fk3_ctl, self.getName("ik_mth"), transform.getTransform(self.ik_ctl)) self.match_ikUpv = primitive.addTransform( self.fk0_ctl, self.getName("upv_mth"), transform.getTransform(self.upv_ctl)) # add visual reference self.line_ref = icon.connection_display_curve( self.getName("visalRef"), [self.upv_ctl, self.knee_ctl])
def addJnt(obj=False, parent=False, noReplace=False, grp=None, jntName=None, *args): """Create one joint for each selected object. Args: obj (bool or dagNode, optional): The object to drive the new joint. If False will use the current selection. parent (bool or dagNode, optional): The parent for the joint. If False will try to parent to jnt_org. If jnt_org doesn't exist will parent the joint under the obj noReplace (bool, optional): If True will add the extension "_jnt" to the new joint name grp (pyNode or None, optional): The set to add the new joint. If none will use "rig_deformers_grp" *args: Maya's dummy Returns: pyNode: The New created joint. """ if not obj: oSel = pm.selected() else: oSel = [obj] for obj in oSel: if not parent: try: oParent = pm.PyNode("jnt_org") except TypeError: oParent = obj else: oParent = parent if not jntName: if noReplace: jntName = "_".join(obj.name().split("_")) + "_jnt" else: jntName = "_".join(obj.name().split("_")[:-1]) + "_jnt" jnt = primitive.addJoint(oParent, jntName, transform.getTransform(obj)) if grp: grp.add(jnt) else: try: defSet = pm.PyNode("rig_deformers_grp") pm.sets(defSet, add=jnt) except TypeError: pm.sets(n="rig_deformers_grp") defSet = pm.PyNode("rig_deformers_grp") pm.sets(defSet, add=jnt) jnt.setAttr("segmentScaleCompensate", False) jnt.setAttr("jointOrient", 0, 0, 0) try: cns_m = applyop.gear_matrix_cns(obj, jnt) # setting the joint orient compensation in order to have clean # rotation channels m = cns_m.drivenRestMatrix.get() tm = datatypes.TransformationMatrix(m) r = datatypes.degrees(tm.getRotation()) jnt.attr("jointOrientX").set(r[0]) jnt.attr("jointOrientY").set(r[1]) jnt.attr("jointOrientZ").set(r[2]) except RuntimeError: for axis in ["tx", "ty", "tz", "rx", "ry", "rz"]: jnt.attr(axis).set(0.0) return jnt
def addJointCtl(self, parent, name, m, color, iconShape, tp=None, lp=True, mirrorConf=[0, 0, 0, 0, 0, 0, 0, 0, 0], guide_loc_ref=None, **kwargs): """ Create the control and apply the shape, if this is alrealdy stored in the guide controllers grp. Args: parent (dagNode): The control parent name (str): The control name. m (matrix): The transfromation matrix for the control. color (int or list of float): The color for the control in index or RGB. iconShape (str): The controls default shape. tp (dagNode): Tag Parent Control object to connect as a parent controller lp (bool): Lock the parent controller channels kwargs (variant): Other arguments for the iconShape type variations Returns: dagNode: The Control. """ if "degree" not in kwargs.keys(): kwargs["degree"] = 1 # print name # fullName = self.getName(name) # remove the _ctl hardcoded in component name if name.endswith("_ctl"): name = name[:-4] # in some situation the name will be only ctl and should be removed # for example control_01 if name.endswith("ctl"): name = name[:-3] # NOTE: this is a dirty workaround to keep backwards compatibility on # control_01 component where the description of the cotrol was just # the ctl suffix. rule = self.options["ctl_name_rule"] if not name: """ if rule == naming.DEFAULT_NAMING_RULE: rule = r"{component}_{side}{index}_{extension}" else: # this ensure we always have name if the naming rule is custom name = "control" """ rule = r"{component}_{side}{index}_{extension}" fullName = self.getName( name, rule=rule, ext="ctl", letter_case=self.options["ctl_description_letter_case"]) bufferName = fullName + "_controlBuffer" if bufferName in self.rig.guide.controllers.keys(): ctl_ref = self.rig.guide.controllers[bufferName] else: ctl_ref = icon.create(parent, fullName + "ref", m, color, iconShape, **kwargs) ctl = primitive.addJoint(parent, fullName, m) ctl.setAttr("drawStyle", 2) # draw none for shape in ctl_ref.getShapes(): ctl.addChild(shape, shape=True, add=True) pm.rename(shape, fullName + "Shape") pm.delete(ctl_ref) icon.setcolor(ctl, color) # add metadata attirbutes. attribute.addAttribute(ctl, "isCtl", "bool", keyable=False) attribute.addAttribute(ctl, "uiHost", "string", keyable=False) ctl.addAttr("uiHost_cnx", at='message', multi=False) # set the control Role for complex components. If the component is # of type control_01 or world_ctl the control role will default to None # since is only one control the role is not needed attribute.addAttribute(ctl, "ctl_role", "string", keyable=False, value=name) # locator reference for quick guide matching # TODO: this is a temporal implementation. We should store the full # guide data in future iterations if guide_loc_ref: attribute.addAttribute(ctl, "guide_loc_ref", "string", keyable=False, value=guide_loc_ref) # mgear name. This keep track of the default shifter name. This naming # system ensure that each control has a unique id. Tools like mirror or # flip pose can use it to track symmetrical controls attribute.addAttribute(ctl, "shifter_name", "string", keyable=False, value=self.getName(name) + "_ctl") attribute.addAttribute(ctl, "side_label", "string", keyable=False, value=self.side) attribute.addAttribute(ctl, "L_custom_side_label", "string", keyable=False, value=self.options["side_left_name"]) attribute.addAttribute(ctl, "R_custom_side_label", "string", keyable=False, value=self.options["side_right_name"]) attribute.addAttribute(ctl, "C_custom_side_label", "string", keyable=False, value=self.options["side_center_name"]) # create the attributes to handlde mirror and symetrical pose attribute.add_mirror_config_channels(ctl, mirrorConf) if self.settings["ctlGrp"]: ctlGrp = self.settings["ctlGrp"] self.addToGroup(ctl, ctlGrp, "controllers") else: ctlGrp = "controllers" self.addToGroup(ctl, ctlGrp) # lock the control parent attributes if is not a control if parent not in self.groups[ctlGrp] and lp: self.transform2Lock.append(parent) # Set the control shapes isHistoricallyInteresting for oShape in ctl.getShapes(): oShape.isHistoricallyInteresting.set(False) # connecting the always draw shapes on top to global attribute if versions.current() >= 20220000: pm.connectAttr(self.rig.ctlXRay_att, oShape.attr("alwaysDrawOnTop")) # set controller tag if versions.current() >= 201650: try: oldTag = pm.PyNode(ctl.name() + "_tag") if not oldTag.controllerObject.connections(): # NOTE: The next line is comment out. Because this will # happend alot since core does't clean # controller tags after deleting the control Object of the # tag. This have been log to Autodesk. # If orphane tags are found, it will be clean in silence. # pm.displayWarning("Orphane Tag: %s will be delete and # created new for: %s"%(oldTag.name(), ctl.name())) pm.delete(oldTag) except TypeError: pass self.add_controller_tag(ctl, tp) self.controlers.append(ctl) return ctl