def addOperators(self): """Create operators and set the relations for the component rig Apply operators/Solvers, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ applyop.aimCns(self.ref_base, self.squash_ctl, axis="yx", wupType=2, wupVector=[1, 0, 0], wupObject=self.ctl, maintainOffset=False) applyop.aimCns(self.ref_squash, self.ctl, axis="-yx", wupType=2, wupVector=[1, 0, 0], wupObject=self.squash_ctl, maintainOffset=False) bIncrement = 1.0 blend = 0 for i, div_cns in enumerate(self.div_cns): intMatrix = applyop.gear_intmatrix_op( self.ref_base.attr("worldMatrix"), self.ref_squash.attr("worldMatrix"), blend) applyop.gear_mulmatrix_op(intMatrix.attr("output"), div_cns.attr("parentInverseMatrix[0]"), div_cns) blend = blend + bIncrement d = vector.getDistance(self.guide.apos[0], self.guide.apos[1]) dist_node = node.createDistNode(self.squash_ctl, self.ctl) rootWorld_node = node.createDecomposeMatrixNode( self.ctl.attr("worldMatrix")) div_node = node.createDivNode(dist_node + ".distance", rootWorld_node + ".outputScaleY") div_node = node.createDivNode(div_node + ".outputX", d) rev_node = node.createReverseNode(div_node + ".outputX") add_node = pm.createNode("plusMinusAverage") add_node.input1D[0].set(1.0) rev_node.outputX >> add_node.input1D[1] div_node.outputX >> self.ref_base.scaleY add_node.output1D >> self.ref_base.scaleX add_node.output1D >> self.ref_base.scaleZ
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ pairs = [[self.top_ctl, self.bottom_npo, 1, 2], [self.bottom_ctl, self.bottom_pivot, 2, 1], [self.ext_ctl, self.int_npo, 3, 4], [self.int_ctl, self.int_pivot, 4, 3]] for pair in pairs: d = vector.getDistance(self.guide.apos[pair[2]], self.guide.apos[pair[3]]) sum_node = node.createPlusMinusAverage1D([d, pair[0].ty]) mul_node = node.createMulNode(pair[0].ty, self.volume_att) sum2_node = node.createPlusMinusAverage1D([d, mul_node.outputX]) mul2_node = node.createDivNode( [sum2_node.output1D, sum_node.output1D, sum2_node.output1D], [d, d, d]) sum3D_node = pm.createNode("plusMinusAverage") sum3D_node.attr("operation").set(2) sum3D_node.input3D[0].input3Dx.set(2) sum3D_node.input3D[0].input3Dz.set(2) mul2_node.outputX >> sum3D_node.input3D[1].input3Dx mul2_node.outputZ >> sum3D_node.input3D[1].input3Dz sum3D_node.output3D.output3Dx >> pair[1].sx mul2_node.outputY >> pair[1].sy sum3D_node.output3D.output3Dx >> pair[1].sz
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref =========================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleLegChainUpvRef"), self.legChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.legChainUpvRef[0], self.ik_ctl, self.upv_cns, mo=True) # Visibilities ------------------------------------- # shape.dispGeometry # fk fkvis_node = node.createReverseNode(self.blend_att) try: for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node.outputX, shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node.outputX, shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node.outputX, shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) except RuntimeError: pm.displayInfo("Visibility already connecte") # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root_ctl, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 else: mulVal = 1 node.createMulNode(self.roll_att, mulVal, o_node + ".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- self.ikhArmRef, self.tmpCrv = applyop.splineIK( self.getName("legRollRef"), self.rollRef, parent=self.root, cParent=self.bone0) pm.pointConstraint(self.mid_ctl, self.tws1_loc, maintainOffset=False) pm.connectAttr(self.mid_ctl.scaleX, self.tws1_loc.scaleX) applyop.oriCns(self.mid_ctl, self.tws1_rot, maintainOffset=False) pm.pointConstraint(self.eff_loc, self.tws2_loc, maintainOffset=False) pm.scaleConstraint(self.eff_loc, self.tws2_loc, maintainOffset=False) applyop.oriCns(self.bone1, self.tws2_loc, maintainOffset=False) applyop.oriCns(self.tws_ref, self.tws2_rot) if self.settings["div0"]: ori_ref = self.rollRef[0] else: ori_ref = self.bone0 applyop.oriCns(ori_ref, self.tws0_loc, maintainOffset=True) self.tws0_loc.setAttr("sx", .001) self.tws2_loc.setAttr("sx", .001) add_node = node.createAddNode(self.roundness_att, .0) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node.distance, distB_node.distance) div_node = node.createDivNode(add_node.output, self.root_ctl.attr("sx")) # comp scaling issue dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node.inputMatrix) div_node2 = node.createDivNode(div_node.outputX, dm_node.outputScaleX) self.volDriver_att = div_node2.outputX if self.settings["extraTweak"]: for tweak_ctl in self.tweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll for i, div_cns in enumerate(self.div_cns): subdiv = 40 if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .501 else: perc = .5 + \ (i - self.settings["div0"] - 1.0) * .5 / \ (self.settings["div1"] + 1.0) perc = max(.0001, min(.999, perc)) # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws2_rot, self.tws1_rot, self.tws0_rot], 1.0 - perc, subdiv) else: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws0_rot, self.tws1_rot, self.tws2_rot], perc, subdiv) pm.connectAttr(self.resample_att, o_node.resample) pm.connectAttr(self.absolute_att, o_node.absolute) # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node.blend) pm.connectAttr(self.volDriver_att, o_node.driver) pm.connectAttr(self.st_att[i], o_node.stretch) pm.connectAttr(self.sq_att[i], o_node.squash) # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref ============================================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleArmChainUpvRef"), self.armChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.armChainUpvRef[0], self.upv_cns, mo=True) # Visibilities ------------------------------------- # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) if self.settings["ikTR"]: for shp in self.ikRot_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.roll_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # Controls ROT order ----------------------------------- attribute.setRotOrder(self.fk0_ctl, "XZY") attribute.setRotOrder(self.fk1_ctl, "XYZ") attribute.setRotOrder(self.fk2_ctl, "YZX") attribute.setRotOrder(self.ik_ctl, "XYZ") # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) # NOTE: Ideally we should not change hierarchy or move object after # object generation method. But is much easier this way since every # part is in the final and correct position # after the ctrn_loc is in the correct position with the ikfk2bone op # point constrain tip reference pm.pointConstraint(self.ik_ctl, self.tip_ref, mo=False) # interpolate transform mid point locator int_matrix = applyop.gear_intmatrix_op( self.armChainUpvRef[0].attr("worldMatrix"), self.tip_ref.attr("worldMatrix"), .5) applyop.gear_mulmatrix_op( int_matrix.attr("output"), self.interpolate_lvl.attr("parentInverseMatrix[0]"), self.interpolate_lvl) # match roll ctl npo to ctrn_loc current transform (so correct orient) transform.matchWorldTransform(self.ctrn_loc, self.roll_ctl_npo) # match roll ctl npo to interpolate transform current position pos = self.interpolate_lvl.getTranslation(space="world") self.roll_ctl_npo.setTranslation(pos, space="world") # parent constraint roll control npo to interpolate trans pm.parentConstraint(self.interpolate_lvl, self.roll_ctl_npo, mo=True) if self.settings["ikTR"]: # connect the control inputs outEff_dm = o_node.listConnections(c=True)[-1][1] inAttr = self.ikRot_npo.attr("translate") outEff_dm.attr("outputTranslate") >> inAttr outEff_dm.attr("outputScale") >> self.ikRot_npo.attr("scale") dm_node = node.createDecomposeMatrixNode(o_node.attr("outB")) dm_node.attr("outputRotate") >> self.ikRot_npo.attr("rotate") # rotation mulM_node = applyop.gear_mulmatrix_op( self.ikRot_ctl.attr("worldMatrix"), self.eff_loc.attr("parentInverseMatrix")) intM_node = applyop.gear_intmatrix_op(o_node.attr("outEff"), mulM_node.attr("output"), o_node.attr("blend")) dm_node = node.createDecomposeMatrixNode(intM_node.attr("output")) dm_node.attr("outputRotate") >> self.eff_loc.attr("rotate") transform.matchWorldTransform(self.fk2_ctl, self.ikRot_cns) # scale: this fix the scalin popping issue intM_node = applyop.gear_intmatrix_op( self.fk2_ctl.attr("worldMatrix"), self.ik_ctl_ref.attr("worldMatrix"), o_node.attr("blend")) mulM_node = applyop.gear_mulmatrix_op( intM_node.attr("output"), self.eff_loc.attr("parentInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulM_node.attr("output")) dm_node.attr("outputScale") >> self.eff_loc.attr("scale") pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 rollMulVal = 1 else: mulVal = 1 rollMulVal = -1 roll_m_node = node.createMulNode(self.roll_att, mulVal) roll_m_node2 = node.createMulNode(self.roll_ctl.attr("rx"), rollMulVal) node.createPlusMinusAverage1D( [roll_m_node.outputX, roll_m_node2.outputX], operation=1, output=o_node + ".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- pm.pointConstraint(self.mid_ctl_twst_ref, self.tws1_npo, maintainOffset=False) pm.connectAttr(self.mid_ctl.scaleX, self.tws1_loc.scaleX) pm.connectAttr(self.mid_ctl.scaleX, self.tws1B_loc.scaleX) pm.orientConstraint(self.mid_ctl_twst_ref, self.tws1_npo, maintainOffset=False) applyop.oriCns(self.mid_ctl, self.tws1_rot, maintainOffset=False) applyop.oriCns(self.mid_ctl, self.tws1B_rot, maintainOffset=False) if self.negate: axis = "xz" axisb = "-xz" else: axis = "-xz" axisb = "xz" applyop.aimCns(self.tws1_loc, self.root, axis=axis, wupType=4, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.tws1B_loc, self.eff_loc, axis=axisb, wupType=4, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) pm.pointConstraint(self.thick_ctl, self.tws1B_loc, maintainOffset=False) o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.tws2_npo.attr("translate")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", self.tws2_npo.attr("rotate")) o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.tws2_rot.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") attribute.setRotOrder(self.tws2_rot, "XYZ") pm.connectAttr(dm_node + ".outputRotate", self.tws2_rot + ".rotate") self.tws0_rot.setAttr("sx", .001) self.tws2_rot.setAttr("sx", .001) add_node = node.createAddNode(self.roundness_att, .001) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) pm.connectAttr(add_node + ".output", self.tws1B_rot.attr("sx")) pm.connectAttr(self.armpit_roll_att, self.tws0_rot + ".rotateX") # Roll Shoulder applyop.splineIK(self.getName("rollRef"), self.rollRef, parent=self.root, cParent=self.bone0) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root.attr("sx")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" if self.settings["extraTweak"]: for tweak_ctl in self.tweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll b_twist = False for i, div_cns in enumerate(self.div_cns): if self.settings["supportJoints"]: if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .49 elif i < (self.settings["div0"] + 3): perc = .50 elif i < (self.settings["div0"] + 4): b_twist = True perc = .51 else: perc = .5 + \ (i - self.settings["div0"] - 3.0) * .5 / \ (self.settings["div1"] + 1.0) else: if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): b_twist = True perc = .501 else: perc = .5 + \ (i - self.settings["div0"] - 1.0) * .5 / \ (self.settings["div1"] + 1.0) perc = max(.001, min(.990, perc)) # mid twist distribution if b_twist: mid_twist = self.tws1B_rot else: mid_twist = self.tws1_rot # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws2_rot, mid_twist, self.tws0_rot], 1.0 - perc, 40) else: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws0_rot, mid_twist, self.tws2_rot], perc, 40) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) if self.settings["ikTR"]: transform.matchWorldTransform(self.ikRot_ctl, self.match_ikRot) transform.matchWorldTransform(self.fk_ctl[2], self.match_fk2) # connect_reader pm.parentConstraint(self.bone0, self.readerA, mo=True) pm.parentConstraint(self.bone1, self.readerB, mo=True) # connect auto thickness if self.negate and not self.settings["mirrorMid"]: d_val = 180 / self.length1 else: d_val = -180 / self.length1 div_thick_node = node.createDivNode(self.elbow_thickness_att, d_val) node.createMulNode(div_thick_node.outputX, self.readerB.ry, self.thick_lvl.tx) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # setup out channels. This channels are pass through for stack node.createPlusMinusAverage1D( [self.mid_wide_att, self.mid_wide_in_att, -1.0], 1, self.mid_wide_out_att) node.createPlusMinusAverage1D( [self.mid_depth_att, self.mid_depth_in_att, -1.0], 1, self.mid_depth_out_att) node.createPlusMinusAverage1D( [self.tip_wide_att, self.tip_wide_in_att, -1.0], 1, self.tip_wide_out_att) node.createPlusMinusAverage1D( [self.tip_depth_att, self.tip_depth_in_att, -1.0], 1, self.tip_depth_out_att) node.createPlusMinusAverage1D( [self.mid_twist_att, self.mid_twist_in_att], 1, self.mid_twist_out_att) node.createPlusMinusAverage1D( [self.tip_twist_att, self.tip_twist_in_att], 1, self.tip_twist_out_att) if not self.settings["simpleFK"]: dm_node_scl = node.createDecomposeMatrixNode(self.root.worldMatrix) if self.settings["keepLength"]: arclen_node = pm.arclen(self.mst_crv, ch=True) alAttr = pm.getAttr(arclen_node + ".arcLength") ration_node = node.createMulNode(self.length_ratio_att, alAttr) pm.addAttr(self.mst_crv, ln="length_ratio", k=True, w=True) node.createDivNode(arclen_node.arcLength, ration_node.outputX, self.mst_crv.length_ratio) div_node_scl = node.createDivNode(self.mst_crv.length_ratio, dm_node_scl.outputScaleX) step = 1.000 / (self.def_number - 1) step_mid = 1.000 / ((self.def_number - 1) / 2.0) u = 0.000 u_mid = 0.000 pass_mid = False for i in range(self.def_number): cnsUpv = applyop.pathCns(self.upv_cns[i], self.upv_crv, cnsType=False, u=u, tangent=False) cns = applyop.pathCns(self.div_cns[i], self.mst_crv, False, u, True) # Connecting the scale for scaling compensation # for axis, AX in zip("xyz", "XYZ"): for axis, AX in zip("x", "X"): pm.connectAttr( dm_node_scl.attr("outputScale{}".format(AX)), self.div_cns[i].attr("s{}".format(axis))) if self.settings["keepLength"]: div_node2 = node.createDivNode(u, div_node_scl.outputX) cond_node = node.createConditionNode( div_node2.input1X, div_node2.outputX, 4, div_node2.input1X, div_node2.outputX) pm.connectAttr(cond_node + ".outColorR", cnsUpv + ".uValue") pm.connectAttr(cond_node + ".outColorR", cns + ".uValue") cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(self.upv_cns[i].attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) # Connect scale Wide and Depth # wide and Depth mid_mul_node = node.createMulNode( [self.mid_wide_att, self.mid_depth_att], [self.mid_wide_in_att, self.mid_depth_in_att]) mid_mul_node2 = node.createMulNode( [mid_mul_node.outputX, mid_mul_node.outputY], [u_mid, u_mid]) mid_mul_node3 = node.createMulNode( [mid_mul_node2.outputX, mid_mul_node2.outputY], [ dm_node_scl.attr("outputScaleX"), dm_node_scl.attr("outputScaleY") ]) tip_mul_node = node.createMulNode( [self.tip_wide_att, self.tip_depth_att], [self.tip_wide_in_att, self.tip_depth_in_att]) tip_mul_node2 = node.createMulNode( [tip_mul_node.outputX, tip_mul_node.outputY], [u, u]) node.createPlusMinusAverage1D([ mid_mul_node3.outputX, 1.0 - u_mid, tip_mul_node2.outputX, 1.0 - u, -1.0 ], 1, self.div_cns[i].attr("sy")) node.createPlusMinusAverage1D([ mid_mul_node3.outputY, 1.0 - u_mid, tip_mul_node2.outputY, 1.0 - u, -1.0 ], 1, self.div_cns[i].attr("sz")) # Connect Twist "cns.frontTwist" twist_mul_node = node.createMulNode( [self.mid_twist_att, self.tip_twist_att], [u_mid, u]) twist_mul_node2 = node.createMulNode( [self.mid_twist_in_att, self.tip_twist_in_att], [u_mid, u]) node.createPlusMinusAverage1D([ twist_mul_node.outputX, twist_mul_node.outputY, twist_mul_node2.outputX, twist_mul_node2.outputY, ], 1, cns.frontTwist) # u_mid calc if u_mid >= 1.0 or pass_mid: u_mid -= step_mid pass_mid = True else: u_mid += step_mid if u_mid > 1.0: u_mid = 1.0 # ensure the tip is never affected byt the mid if i == (self.def_number - 1): u_mid = 0.0 u += step if self.settings["keepLength"]: # add the safty distance offset self.tweakTip_npo.attr("tx").set(self.off_dist) # connect vis line ref for shp in self.line_ref.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) # CONNECT STACK # master components mstr_global = self.settings["masterChainGlobal"] mstr_local = self.settings["masterChainLocal"] if mstr_global: mstr_global = self.rig.components[mstr_global] if mstr_local: mstr_local = self.rig.components[mstr_local] # connect twist and scale if mstr_global and mstr_local: node.createPlusMinusAverage1D([ mstr_global.root.mid_twist_out, mstr_local.root.mid_twist_out ], 1, self.mid_twist_in_att) node.createPlusMinusAverage1D([ mstr_global.root.tip_twist_out, mstr_local.root.tip_twist_out ], 1, self.tip_twist_in_att) node.createPlusMinusAverage1D([ mstr_global.root.mid_wide_out, mstr_local.root.mid_wide_out, -1 ], 1, self.mid_wide_in_att) node.createPlusMinusAverage1D([ mstr_global.root.tip_wide_out, mstr_local.root.tip_wide_out, -1 ], 1, self.tip_wide_in_att) node.createPlusMinusAverage1D([ mstr_global.root.mid_depth_out, mstr_local.root.mid_depth_out, -1 ], 1, self.mid_depth_in_att) node.createPlusMinusAverage1D([ mstr_global.root.tip_depth_out, mstr_local.root.tip_depth_out, -1 ], 1, self.tip_depth_in_att) elif mstr_local or mstr_global: for master_chain in [mstr_local, mstr_global]: if master_chain: pm.connectAttr(master_chain.root.mid_twist_out, self.mid_twist_in_att) pm.connectAttr(master_chain.root.tip_twist_out, self.tip_twist_in_att) pm.connectAttr(master_chain.root.mid_wide_out, self.mid_wide_in_att) pm.connectAttr(master_chain.root.tip_wide_out, self.tip_wide_in_att) pm.connectAttr(master_chain.root.mid_depth_out, self.mid_depth_in_att) pm.connectAttr(master_chain.root.tip_depth_out, self.tip_depth_in_att) # connect the fk chain ctls for e, ctl in enumerate(self.fk_ctl): # connect out out_loc = self.fk_local_out[e] applyop.gear_mulmatrix_op(ctl.attr("worldMatrix"), out_loc.attr("parentInverseMatrix[0]"), out_loc) out_glob = self.fk_global_out[e] out_ref = self.fk_global_ref[e] applyop.gear_mulmatrix_op(out_ref.attr("worldMatrix"), out_glob.attr("parentInverseMatrix[0]"), out_glob) # connect in global if mstr_global: self.connect_master(mstr_global.fk_global_out, self.fk_global_in, e, self.settings["cnxOffset"]) # connect in local if mstr_local: self.connect_master(mstr_local.fk_local_out, self.fk_local_in, e, self.settings["cnxOffset"]) for shp in ctl.getShapes(): pm.connectAttr(self.fkVis_att, shp.attr("visibility")) for ctl in self.tweak_ctl: for shp in ctl.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) if self.settings["extraTweak"]: for tweak_ctl in self.extratweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility"))
def addFkOperator(self, i, rootWorld_node, crv_node): if i == 0 and self.settings["isSplitHip"]: s = self.fk_hip_ctl d = self.fk_local_npo[0], # maintainOffset, skipRotate, skipTranslate _ = pm.parentConstraint(s, d, mo=True, sr=("x", "y", "z"), st=()) s = self.ik_global_out[0] d = self.hip_fk_local_in, # maintainOffset, skipRotate, skipTranslate pm.parentConstraint(s, d, mo=True) # break FK hierarchical orient if i not in [len(self.guide.apos), 0]: s = self.fk_ctl[i - 1] s2 = self.fk_npo[i] d = self.fk_local_npo[i] mulmat_node = applyop.gear_mulmatrix_op(s2.attr("matrix"), s.attr("matrix")) mulmat_node2 = applyop.gear_mulmatrix_op(mulmat_node.attr("output"), s2.attr("inverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node2 + ".output") pm.connectAttr(dm_node + ".outputTranslate", d.attr("t")) check_list = (pm.Attribute, unicode, str) # noqa cond = pm.createNode("condition") pm.setAttr(cond + ".operation", 4) # greater attribute.connectSet(self.fk_collapsed_att, cond + ".secondTerm", check_list) attribute.connectSet(dm_node + ".outputRotate", cond + ".colorIfTrue", check_list) pm.setAttr(cond + ".colorIfFalseR", 0.) pm.setAttr(cond + ".colorIfFalseG", 0.) pm.setAttr(cond + ".colorIfFalseB", 0.) pm.connectAttr(cond + ".outColor", d.attr("r")) # References if i == 0: # we add extra 10% to the first position u = (1.0 / (len(self.guide.apos) - 1.0)) / 1000 else: u = getCurveUAtPoint(self.slv_crv, self.guide.apos[i]) tmp_div_npo_transform = getTransform(self.div_cns_npo[i]) # to fix mismatch before/after later cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 0) # front axis is 'X' # Roll # choose ik_ctls for _i, uv in enumerate(self.ik_uv_param): if u < uv: ik_a = self.ik_ctl[_i - 1] ik_b = self.ik_ctl[_i] if self.settings["isSplitHip"] and i == 0: u = (i + 1) / (len(self.guide.apos) - 1.0) ratio = u / uv * .5 else: ratio = u / uv break else: ik_a = self.ik_ctl[-2] ik_b = self.ik_ctl[-1] ratio = 1. intMatrix = applyop.gear_intmatrix_op( ik_a + ".worldMatrix", ik_b + ".worldMatrix", ratio) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") self.div_cns_npo[i].setMatrix(tmp_div_npo_transform, worldSpace=True) # compensate scale reference div_node = node.createDivNode( [1, 1, 1], [rootWorld_node + ".outputScaleX", rootWorld_node + ".outputScaleY", rootWorld_node + ".outputScaleZ"]) # Squash n Stretch op = applyop.gear_squashstretch2_op(self.scl_transforms[i], self.root, pm.arclen(self.slv_crv), "y", div_node + ".output") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") pm.connectAttr(self.st_att[i], op + ".stretch") pm.connectAttr(self.sq_att[i], op + ".squash") # Controlers tmp_local_npo_transform = getTransform(self.fk_local_npo[i]) # to fix mismatch before/after later if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns_npo[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) else: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns_npo[i].attr("worldMatrix"), self.div_cns_npo[i - 1].attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") mul_node = node.createMulNode(div_node + ".output", dm_node + ".outputTranslate") pm.connectAttr(mul_node + ".output", self.fk_npo[i].attr("t")) pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) self.addOperatorsOrientationLock(i, cns) self.fk_local_npo[i].setMatrix(tmp_local_npo_transform, worldSpace=True) # References if i < (len(self.fk_ctl) - 1): aim = pm.aimConstraint(self.div_cns_npo[i + 1], self.div_cns_npo[i], maintainOffset=False) pm.setAttr(aim + ".aimVectorX", 0) pm.setAttr(aim + ".aimVectorY", 1) pm.setAttr(aim + ".aimVectorZ", 0) pm.setAttr(aim + ".upVectorX", 0) pm.setAttr(aim + ".upVectorY", 1) pm.setAttr(aim + ".upVectorZ", 0)
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ dm_node_scl = node.createDecomposeMatrixNode(self.root.worldMatrix) if self.settings["keepLength"]: arclen_node = pm.arclen(self.mst_crv, ch=True) alAttr = pm.getAttr(arclen_node + ".arcLength") ration_node = node.createMulNode(self.length_ratio_att, alAttr) pm.addAttr(self.mst_crv, ln="length_ratio", k=True, w=True) node.createDivNode(arclen_node.arcLength, ration_node.outputX, self.mst_crv.length_ratio) div_node_scl = node.createDivNode(self.mst_crv.length_ratio, dm_node_scl.outputScaleX) step = 1.000 / (self.def_number - 1) u = 0.000 for i in range(self.def_number): cnsUpv = applyop.pathCns(self.upv_cns[i], self.upv_crv, cnsType=False, u=u, tangent=False) cns = applyop.pathCns(self.div_cns[i], self.mst_crv, False, u, True) # Connectiong the scale for scaling compensation for axis, AX in zip("xyz", "XYZ"): pm.connectAttr(dm_node_scl.attr("outputScale{}".format(AX)), self.div_cns[i].attr("s{}".format(axis))) if self.settings["keepLength"]: div_node2 = node.createDivNode(u, div_node_scl.outputX) cond_node = node.createConditionNode(div_node2.input1X, div_node2.outputX, 4, div_node2.input1X, div_node2.outputX) pm.connectAttr(cond_node + ".outColorR", cnsUpv + ".uValue") pm.connectAttr(cond_node + ".outColorR", cns + ".uValue") cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(self.upv_cns[i].attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) u += step if self.settings["keepLength"]: # add the safty distance offset self.tweakTip_npo.attr("tx").set(self.off_dist) # connect vis line ref for shp in self.line_ref.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) for ctl in self.tweak_ctl: for shp in ctl.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) for ctl in self.fk_ctl: for shp in ctl.getShapes(): pm.connectAttr(self.fkVis_att, shp.attr("visibility")) if self.settings["extraTweak"]: for tweak_ctl in self.extratweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility"))
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Visibilities ------------------------------------- # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk0_roll_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_roll_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) fkvis2_node = node.createReverseNode(self.blend2_att) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis2_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # jnt ctl for ctl in (self.div_ctls): for shp in ctl.getShapes(): pm.connectAttr(self.jntctl_vis_att, shp.attr("visibility")) # Controls ROT order ----------------------------------- attribute.setRotOrder(self.fk0_ctl, "YZX") attribute.setRotOrder(self.fk1_ctl, "XYZ") attribute.setRotOrder(self.fk2_ctl, "YZX") attribute.setRotOrder(self.ik_ctl, "XYZ") # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_npo] o_node = applyop.gear_ikfk2bone_op(out, self.root, self.ik_ref, self.upv_ctl, self.fk0_mtx, self.fk1_mtx, self.fk2_mtx, self.length0, self.length1, self.negate) pm.connectAttr(self.blend_att, o_node + ".blend") pm.connectAttr(self.roll_att, o_node + ".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # update issue on effector scale interpol, disconnect for stability pm.disconnectAttr(self.eff_npo.scale) # auto upvector ------------------------------------- if self.negate: o_node = applyop.aimCns(self.upv_auv, self.ik_ctl, axis="-xy", wupType=4, wupVector=[0, 1, 0], wupObject=self.upv_auv, maintainOffset=False) else: o_node = applyop.aimCns(self.upv_auv, self.ik_ctl, axis="xy", wupType=4, wupVector=[0, 1, 0], wupObject=self.upv_auv, maintainOffset=False) o_node = applyop.gear_mulmatrix_op( self.upv_auv.attr("worldMatrix"), self.upv_mtx.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pb_node = pm.createNode("pairBlend") pb_node.attr("rotInterpolation").set(1) pm.connectAttr(dm_node + ".outputTranslate", pb_node + ".inTranslate2") pm.connectAttr(dm_node + ".outputRotate", pb_node + ".inRotate2") pm.connectAttr(pb_node + ".outRotate", self.upv_mtx.attr("rotate")) pm.connectAttr(pb_node + ".outTranslate", self.upv_mtx.attr("translate")) pm.connectAttr(self.auv_att, pb_node + ".weight") # fk0 mtx connection o_node = applyop.gear_mulmatrix_op( self.fk0_roll_ctl.attr("worldMatrix"), self.fk0_mtx.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.fk0_mtx.attr("translate")) pm.connectAttr(dm_node + ".outputRotate", self.fk0_mtx.attr("rotate")) # fk1 loc connect to fk1 ref @ pos and rot, not scl to avoid shearing o_node = applyop.gear_mulmatrix_op( self.fk1_ref.attr("worldMatrix"), self.fk1_loc.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.fk1_loc.attr("translate")) pm.connectAttr(dm_node + ".outputRotate", self.fk1_loc.attr("rotate")) # fk1 mtx orient cns to fk1 roll pm.connectAttr(self.fk1_roll_ctl.attr("rotate"), self.fk1_mtx.attr("rotate")) # fk2_loc position constraint to effector------------------------ o_node = applyop.gear_mulmatrix_op( self.eff_npo.attr("worldMatrix"), self.fk2_loc.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.fk2_loc.attr("translate")) # fk2_loc rotation constraint to bone1 (bugfixed) -------------- o_node = applyop.gear_mulmatrix_op( self.bone1.attr("worldMatrix"), self.fk2_loc.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", self.fk2_loc.attr("rotate")) # hand ikfk blending from fk ref to ik ref (serious bugfix)-------- o_node = applyop.gear_mulmatrix_op( self.fk_ref.attr("worldMatrix"), self.eff_loc.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pb_node = pm.createNode("pairBlend") pb_node.attr("rotInterpolation").set(1) pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", pb_node + ".inRotate1") pm.connectAttr(self.blend2_att, pb_node + ".weight") pm.connectAttr(pb_node + ".outRotate", self.eff_loc.attr("rotate")) o_node = applyop.gear_mulmatrix_op( self.ik_ref.attr("worldMatrix"), self.eff_loc.attr("parentInverseMatrix")) dm_node1 = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node1 + ".inputMatrix") pm.connectAttr(dm_node1 + ".outputRotate", pb_node + ".inRotate2") # use blendcolors to blend scale bc_node = pm.createNode("blendColors") pm.connectAttr(self.blend_att, bc_node + ".blender") pm.connectAttr(dm_node + ".outputScale", bc_node + ".color2") pm.connectAttr(dm_node1 + ".outputScale", bc_node + ".color1") pm.connectAttr(bc_node + ".output", self.eff_loc.attr("scale")) # Twist references --------------------------------- pm.connectAttr(self.mid_ctl.attr("translate"), self.tws1_npo.attr("translate")) pm.connectAttr(self.mid_ctl.attr("rotate"), self.tws1_npo.attr("rotate")) pm.connectAttr(self.mid_ctl.attr("scale"), self.tws1_npo.attr("scale")) o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.tws3_npo.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.tws3_npo.attr("translate")) pm.connectAttr(dm_node + ".outputRotate", self.tws3_npo.attr("rotate")) attribute.setRotOrder(self.tws3_rot, "XYZ") # elbow thickness connection if self.negate: o_node = node.createMulNode( [self.elbow_thickness_att, self.elbow_thickness_att], [0.5, -0.5, 0], [self.tws1_loc + ".translateX", self.tws2_loc + ".translateX"]) else: o_node = node.createMulNode( [self.elbow_thickness_att, self.elbow_thickness_att], [-0.5, 0.5, 0], [self.tws1_loc + ".translateX", self.tws2_loc + ".translateX"]) # connect both tws1 and tws2 (mid tws) self.tws0_rot.setAttr("sx", .001) self.tws3_rot.setAttr("sx", .001) add_node = node.createAddNode(self.roundness0_att, .001) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) add_node = node.createAddNode(self.roundness1_att, .001) pm.connectAttr(add_node + ".output", self.tws2_rot.attr("sx")) pm.connectAttr(self.armpit_roll_att, self.tws0_rot + ".rotateX") # Roll Shoulder--use aimconstraint withour uovwctor to solve # the stable twist if self.negate: o_node = applyop.aimCns(self.tws0_loc, self.mid_ctl, axis="-xy", wupType=4, wupVector=[0, 1, 0], wupObject=self.tws0_npo, maintainOffset=False) else: o_node = applyop.aimCns(self.tws0_loc, self.mid_ctl, axis="xy", wupType=4, wupVector=[0, 1, 0], wupObject=self.tws0_npo, maintainOffset=False) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_npo) distB_node = node.createDistNode(self.tws1_npo, self.tws3_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root.attr("sx")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" # Divisions ---------------------------------------- # div mid constraint to mid ctl o_node = applyop.gear_mulmatrix_op( self.mid_ctl.attr("worldMatrix"), self.div_mid.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.div_mid.attr("translate")) pm.connectAttr(dm_node + ".outputRotate", self.div_mid.attr("rotate")) # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll # linear scaling percentage (1) to effector (2) to elbow scl_1_perc = [] scl_2_perc = [] for i, div_cnsUp in enumerate(self.div_cnsUp): if i < (self.settings["div0"] + 1): perc = i / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .95 perc = max(.001, min(.99, perc)) # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cnsUp, [self.tws1_rot, self.tws0_rot], 1 - perc, 20) else: o_node = applyop.gear_rollsplinekine_op( div_cnsUp, [self.tws0_rot, self.tws1_rot], perc, 20) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") scl_1_perc.append(perc / 2) scl_2_perc.append(perc) scl_1_perc.append(0.5) scl_2_perc.append(1) for i, div_cnsDn in enumerate(self.div_cnsDn): if i == (0): perc = .05 elif i < (self.settings["div1"] + 1): perc = i / (self.settings["div1"] + 1.0) elif i < (self.settings["div1"] + 2): perc = .95 perc = max(.001, min(.990, perc)) # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cnsDn, [self.tws3_rot, self.tws2_rot], 1 - perc, 20) else: o_node = applyop.gear_rollsplinekine_op( div_cnsDn, [self.tws2_rot, self.tws3_rot], perc, 20) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") scl_1_perc.append(perc / 2 + 0.5) scl_2_perc.append(1 - perc) # Squash n Stretch for i, div_cns in enumerate(self.div_cns): o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # get the first mult_node after sq op mult_node = pm.listHistory(o_node, future=True)[1] # linear blend effector scale bc_node = pm.createNode("blendColors") bc_node.setAttr("color2R", 1) bc_node.setAttr("color2G", 1) bc_node.setAttr("blender", scl_1_perc[i]) pm.connectAttr(self.eff_loc.attr("scale"), bc_node + ".color1") # linear blend mid scale bc_node2 = pm.createNode("blendColors") bc_node2.setAttr("color2R", 1) bc_node2.setAttr("color2G", 1) bc_node2.setAttr("blender", scl_2_perc[i]) pm.connectAttr(self.mid_ctl.attr("scale"), bc_node2 + ".color1") # mid_ctl scale * effector scale mult_node2 = pm.createNode("multiplyDivide") pm.connectAttr(bc_node2 + ".output", mult_node2 + ".input1") pm.connectAttr(bc_node + ".output", mult_node2 + ".input2") # plug to sq scale pm.connectAttr(mult_node2 + ".output", mult_node + ".input2") # match IK/FK ref pm.connectAttr(self.bone0.attr("rotate"), self.match_fk0.attr("rotate")) pm.connectAttr(self.bone0.attr("translate"), self.match_fk0.attr("translate")) pm.connectAttr(self.bone1.attr("rotate"), self.match_fk1.attr("rotate")) pm.connectAttr(self.bone1.attr("translate"), self.match_fk1.attr("translate")) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Soft condition soft_cond_node = node.createConditionNode( self.soft_attr, 0.0001, 4, 0.0001, self.soft_attr) self.soft_attr_cond = soft_cond_node.outColorR if self.settings["ikSolver"]: self.ikSolver = "ikRPsolver" else: pm.mel.eval("ikSpringSolver;") self.ikSolver = "ikSpringSolver" # 1 bone chain Upv ref =============================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleLegChainUpvRef"), self.legChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.legChainUpvRef[0], self.upv_cns, mo=True) # mid joints ================================================ for xjnt, midJ in zip(self.legBones[1:3], [self.mid1_jnt, self.mid2_jnt]): node.createPairBlend(None, xjnt, .5, 1, midJ) pm.connectAttr(xjnt + ".translate", midJ + ".translate", f=True) pm.parentConstraint(self.mid1_jnt, self.knee_lvl) pm.parentConstraint(self.mid2_jnt, self.ankle_lvl) # joint length multiply multJnt1_node = node.createMulNode(self.boneALenght_attr, self.boneALenghtMult_attr) multJnt2_node = node.createMulNode(self.boneBLenght_attr, self.boneBLenghtMult_attr) multJnt3_node = node.createMulNode(self.boneCLenght_attr, self.boneCLenghtMult_attr) # # IK 3 bones =============================================== self.ikHandle = primitive.addIkHandle(self.softblendLoc, self.getName("ik3BonesHandle"), self.chain3bones, self.ikSolver, self.upv_ctl) # TwistTest if [round(elem, 4) for elem in transform.getTranslation(self.chain3bones[1])] \ != [round(elem, 4) for elem in self.guide.apos[1]]: add_nodeTwist = node.createAddNode(180.0, self.roll_att) else: add_nodeTwist = node.createAddNode(0, self.roll_att) if self.negate: mulVal = 1 else: mulVal = -1 node.createMulNode( add_nodeTwist + ".output", mulVal, self.ikHandle.attr("twist")) # stable spring solver doble rotation pm.pointConstraint(self.root_ctl, self.chain3bones[0]) # softIK 3 bones operators applyop.aimCns(self.aim_tra, self.ik_ref, axis="zx", wupType=4, wupVector=[1, 0, 0], wupObject=self.root_ctl, maintainOffset=False) plusTotalLength_node = node.createPlusMinusAverage1D( [multJnt1_node.attr("outputX"), multJnt2_node.attr("outputX"), multJnt3_node.attr("outputX")]) subtract1_node = node.createPlusMinusAverage1D( [plusTotalLength_node.attr("output1D"), self.soft_attr_cond], 2) distance1_node = node.createDistNode(self.ik_ref, self.aim_tra) div1_node = node.createDivNode(1.0, self.rig.global_ctl + ".sx") mult1_node = node.createMulNode(distance1_node + ".distance", div1_node + ".outputX") subtract2_node = node.createPlusMinusAverage1D( [mult1_node.attr("outputX"), subtract1_node.attr("output1D")], 2) div2_node = node.createDivNode(subtract2_node + ".output1D", self.soft_attr_cond) mult2_node = node.createMulNode(-1, div2_node + ".outputX") power_node = node.createPowNode(self.softSpeed_attr, mult2_node + ".outputX") mult3_node = node.createMulNode(self.soft_attr_cond, power_node + ".outputX") subtract3_node = node.createPlusMinusAverage1D( [plusTotalLength_node.attr("output1D"), mult3_node.attr("outputX")], 2) cond1_node = node.createConditionNode( self.soft_attr_cond, 0, 2, subtract3_node + ".output1D", plusTotalLength_node + ".output1D") cond2_node = node.createConditionNode(mult1_node + ".outputX", subtract1_node + ".output1D", 2, cond1_node + ".outColorR", mult1_node + ".outputX") pm.connectAttr(cond2_node + ".outColorR", self.wristSoftIK + ".tz") # soft blend pc_node = pm.pointConstraint(self.wristSoftIK, self.ik_ref, self.softblendLoc) node.createReverseNode(self.stretch_attr, pc_node + ".target[0].targetWeight") pm.connectAttr(self.stretch_attr, pc_node + ".target[1].targetWeight", f=True) # Stretch distance2_node = node.createDistNode(self.softblendLoc, self.wristSoftIK) mult4_node = node.createMulNode(distance2_node + ".distance", div1_node + ".outputX") # bones for i, mulNode in enumerate([multJnt1_node, multJnt2_node, multJnt3_node]): div3_node = node.createDivNode(mulNode + ".outputX", plusTotalLength_node + ".output1D") mult5_node = node.createMulNode(mult4_node + ".outputX", div3_node + ".outputX") mult6_node = node.createMulNode(self.stretch_attr, mult5_node + ".outputX") node.createPlusMinusAverage1D( [mulNode.attr("outputX"), mult6_node.attr("outputX")], 1, self.chain3bones[i + 1] + ".tx") # IK 2 bones =============================================== self.ikHandle2 = primitive.addIkHandle(self.softblendLoc2, self.getName("ik2BonesHandle"), self.chain2bones, self.ikSolver, self.upv_ctl) node.createMulNode(self.roll_att, mulVal, self.ikHandle2.attr("twist")) # stable spring solver doble rotation pm.pointConstraint(self.root_ctl, self.chain2bones[0]) parentc_node = pm.parentConstraint( self.ik2b_ikCtl_ref, self.ik2b_bone_ref, self.ik2b_blend) node.createReverseNode(self.fullIK_attr, parentc_node + ".target[0].targetWeight") pm.connectAttr(self.fullIK_attr, parentc_node + ".target[1].targetWeight", f=True) # softIK 2 bones operators applyop.aimCns(self.aim_tra2, self.ik2b_ik_ref, axis="zx", wupType=4, wupVector=[1, 0, 0], wupObject=self.root_ctl, maintainOffset=False) plusTotalLength_node = node.createPlusMinusAverage1D( [multJnt1_node.attr("outputX"), multJnt2_node.attr("outputX")]) subtract1_node = node.createPlusMinusAverage1D( [plusTotalLength_node.attr("output1D"), self.soft_attr_cond], 2) distance1_node = node.createDistNode(self.ik2b_ik_ref, self.aim_tra2) div1_node = node.createDivNode(1, self.rig.global_ctl + ".sx") mult1_node = node.createMulNode(distance1_node + ".distance", div1_node + ".outputX") subtract2_node = node.createPlusMinusAverage1D( [mult1_node.attr("outputX"), subtract1_node.attr("output1D")], 2) div2_node = node.createDivNode(subtract2_node + ".output1D", self.soft_attr_cond) mult2_node = node.createMulNode(-1, div2_node + ".outputX") power_node = node.createPowNode(self.softSpeed_attr, mult2_node + ".outputX") mult3_node = node.createMulNode(self.soft_attr_cond, power_node + ".outputX") subtract3_node = node.createPlusMinusAverage1D( [plusTotalLength_node.attr("output1D"), mult3_node.attr("outputX")], 2) cond1_node = node.createConditionNode( self.soft_attr_cond, 0, 2, subtract3_node + ".output1D", plusTotalLength_node + ".output1D") cond2_node = node.createConditionNode(mult1_node + ".outputX", subtract1_node + ".output1D", 2, cond1_node + ".outColorR", mult1_node + ".outputX") pm.connectAttr(cond2_node + ".outColorR", self.ankleSoftIK + ".tz") # soft blend pc_node = pm.pointConstraint(self.ankleSoftIK, self.ik2b_ik_ref, self.softblendLoc2) node.createReverseNode(self.stretch_attr, pc_node + ".target[0].targetWeight") pm.connectAttr(self.stretch_attr, pc_node + ".target[1].targetWeight", f=True) # Stretch distance2_node = node.createDistNode(self.softblendLoc2, self.ankleSoftIK) mult4_node = node.createMulNode(distance2_node + ".distance", div1_node + ".outputX") for i, mulNode in enumerate([multJnt1_node, multJnt2_node]): div3_node = node.createDivNode(mulNode + ".outputX", plusTotalLength_node + ".output1D") mult5_node = node.createMulNode(mult4_node + ".outputX", div3_node + ".outputX") mult6_node = node.createMulNode(self.stretch_attr, mult5_node + ".outputX") node.createPlusMinusAverage1D([mulNode.attr("outputX"), mult6_node.attr("outputX")], 1, self.chain2bones[i + 1] + ".tx") # IK/FK connections for i, x in enumerate(self.fk_ctl): pm.parentConstraint(x, self.legBonesFK[i], mo=True) for i, x in enumerate([self.chain2bones[0], self.chain2bones[1]]): pm.parentConstraint(x, self.legBonesIK[i], mo=True) pm.pointConstraint(self.ik2b_ik_ref, self.legBonesIK[2]) applyop.aimCns(self.legBonesIK[2], self.roll_ctl, axis="xy", wupType=4, wupVector=[0, 1, 0], wupObject=self.legBonesIK[1], maintainOffset=False) pm.connectAttr(self.chain3bones[-1].attr("tx"), self.legBonesIK[-1].attr("tx")) # foot twist roll pm.orientConstraint(self.ik_ref, self.legBonesIK[-1], mo=True) node.createMulNode( -1, self.chain3bones[-1].attr("tx"), self.ik2b_ik_ref.attr("tx")) for i, x in enumerate(self.legBones): node.createPairBlend( self.legBonesFK[i], self.legBonesIK[i], self.blend_att, 1, x) # Twist references ---------------------------------------- self.ikhArmRef, self.tmpCrv = applyop.splineIK( self.getName("legRollRef"), self.rollRef, parent=self.root, cParent=self.legBones[0]) initRound = .001 multVal = 1 multTangent_node = node.createMulNode(self.roundnessKnee_att, multVal) add_node = node.createAddNode(multTangent_node + ".outputX", initRound) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) for x in ["translate"]: pm.connectAttr(self.knee_ctl.attr(x), self.tws1_loc.attr(x)) for x in "xy": pm.connectAttr(self.knee_ctl.attr("r" + x), self.tws1_loc.attr("r" + x)) multTangent_node = node.createMulNode(self.roundnessAnkle_att, multVal) add_node = node.createAddNode(multTangent_node + ".outputX", initRound) pm.connectAttr(add_node + ".output", self.tws2_rot.attr("sx")) for x in ["translate"]: pm.connectAttr(self.ankle_ctl.attr(x), self.tws2_loc.attr(x)) for x in "xy": pm.connectAttr(self.ankle_ctl.attr("r" + x), self.tws2_loc.attr("r" + x)) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) distC_node = node.createDistNode(self.tws2_loc, self.tws3_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") add_node2 = node.createAddNode(distC_node + ".distance", add_node + ".output") div_node = node.createDivNode(add_node2 + ".output", self.root_ctl.attr("sx")) # comp scaling dm_node = node.createDecomposeMatrixNode(self.root.attr("worldMatrix")) div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" # Flip Offset ---------------------------------------- pm.connectAttr(self.ankleFlipOffset_att, self.tws2_loc.attr("rz")) pm.connectAttr(self.kneeFlipOffset_att, self.tws1_loc.attr("rz")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll for i, div_cns in enumerate(self.div_cns): subdiv = False if i == len(self.div_cns) - 1 or i == 0: subdiv = 45 else: subdiv = 45 if i < (self.settings["div0"] + 1): perc = i * .333 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + self.settings["div1"] + 2): perc = i * .333 / (self.settings["div0"] + 1.0) else: perc = (.5 + (i - self.settings["div0"] - 3.0) * .5 / (self.settings["div1"] + 1.0)) if i < (self.settings["div0"] + 2): perc = i * .333 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + self.settings["div1"] + 3): perc = (.333 + (i - self.settings["div0"] - 1) * .333 / (self.settings["div1"] + 1.0)) else: perc = (.666 + (i - self.settings["div1"] - self.settings["div0"] - 2.0) * .333 / (self.settings["div2"] + 1.0)) # we neet to offset the ankle and knee point to force the bone # orientation to the nex bone span if perc == .333: perc = .3338 elif perc == .666: perc = .6669 perc = max(.001, min(.999, perc)) # Roll cts = [self.tws0_rot, self.tws1_rot, self.tws2_rot, self.tws3_rot] o_node = applyop.gear_rollsplinekine_op(div_cns, cts, perc, subdiv) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # Visibilities ------------------------------------- # fk fkvis_node = node.createReverseNode(self.blend_att) for ctrl in self.fk_ctl: for shp in ctrl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for ctrl in [self.ik_ctl, self.roll_ctl, self.upv_ctl, self.line_ref]: for shp in ctrl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # setup leg o_node scale compensate pm.connectAttr(self.rig.global_ctl + ".scale", self.setup + ".scale") # match IK/FK ref pm.parentConstraint(self.legBones[0], self.match_fk0_off, mo=True) pm.parentConstraint(self.legBones[1], self.match_fk1_off, mo=True) pm.parentConstraint(self.legBones[2], self.match_fk2_off, mo=True) return
def addFkOperator(self, i, rootWorld_node, crv_node): fk_local_npo_xfoms = [] if i not in [len(self.guide.apos), 0]: xform = getTransform(self.fk_local_npo[i]) fk_local_npo_xfoms.append(xform) # break FK hierarchical orient if i not in [len(self.guide.apos), 0]: s = self.fk_ctl[i - 1] s2 = self.fk_npo[i] d = self.fk_local_npo[i] mulmat_node = applyop.gear_mulmatrix_op(s2.attr("matrix"), s.attr("matrix")) mulmat_node2 = applyop.gear_mulmatrix_op(mulmat_node.attr("output"), s2.attr("inverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node2 + ".output") pm.connectAttr(dm_node + ".outputTranslate", d.attr("t")) check_list = (pm.Attribute, unicode, str) # noqa cond = pm.createNode("condition") pm.setAttr(cond + ".operation", 4) # greater attribute.connectSet(self.fk_collapsed_att, cond + ".secondTerm", check_list) attribute.connectSet(dm_node + ".outputRotate", cond + ".colorIfTrue", check_list) pm.setAttr(cond + ".colorIfFalseR", 0.) pm.setAttr(cond + ".colorIfFalseG", 0.) pm.setAttr(cond + ".colorIfFalseB", 0.) pm.connectAttr(cond + ".outColor", d.attr("r")) # References if i == 0: # we add extra 10% to the first position u = (1.0 / (len(self.guide.apos) - 1.0)) / 10000 else: u = getCurveUAtPoint(self.slv_crv, self.guide.apos[i]) tmp_div_npo_transform = getTransform(self.div_cns_npo[i]) # to fix mismatch before/after later cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 0) # front axis is 'X' # Roll # choose ik_ctls for _i, uv in enumerate(self.ik_uv_param): if u < uv: ik_a = self.ik_ctl[_i - 1] ik_b = self.ik_ctl[_i] roll_a = self.ik_decompose_rot[_i - 1] roll_b = self.ik_decompose_rot[_i] ratio = (uv - u) * (self.settings["ikNb"] - 1) break else: ik_a = self.ik_ctl[-2] ik_b = self.ik_ctl[-1] roll_a = self.ik_decompose_rot[-2] roll_b = self.ik_decompose_rot[-1] ratio = 1. intMatrix = applyop.gear_intmatrix_op( ik_a + ".worldMatrix", ik_b + ".worldMatrix", ratio) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") # pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") self.div_cns_npo[i].setMatrix(tmp_div_npo_transform, worldSpace=True) # rotationdriver roll_ratio = (i + 1.00) / len(self.fk_ctl) mul1 = pm.createNode("multDoubleLinear") pm.connectAttr(roll_a.attr("outRoll"), mul1.attr("input1")) pm.setAttr(mul1.attr("input2"), ratio) mul2 = pm.createNode("multDoubleLinear") pm.connectAttr(roll_b.attr("outRoll"), mul2.attr("input1")) pm.setAttr(mul2.attr("input2"), (1. - ratio)) add = pm.createNode("addDoubleLinear") pm.connectAttr(mul1.attr("output"), add.attr("input1")) pm.connectAttr(mul2.attr("output"), add.attr("input2")) compose_rot = pm.createNode("composeRotate") pm.setAttr(compose_rot.attr("axisOrientX"), 90.0) pm.setAttr(compose_rot.attr("axisOrientZ"), 90.0) pm.connectAttr(add.attr("output"), compose_rot.attr("roll")) pm.connectAttr(compose_rot.attr("outRotate"), self.div_roll_npo[i].attr("rotate")) # compensate scale reference div_node = node.createDivNode( [1, 1, 1], [rootWorld_node + ".outputScaleX", rootWorld_node + ".outputScaleY", rootWorld_node + ".outputScaleZ"]) # Squash n Stretch op = applyop.gear_squashstretch2_op(self.scl_transforms[i], self.root, pm.arclen(self.slv_crv), "y", div_node + ".output") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") # pm.connectAttr(self.st_att[i], op + ".stretch") # pm.connectAttr(self.sq_att[i], op + ".squash") # Controlers tmp_local_npo_transform = getTransform(self.fk_local_npo[i]) # to fix mismatch before/after later if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_roll_npo[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) elif i != len(self.guide.apos) - 1: mulmat_node = applyop.gear_mulmatrix_op( self.div_roll_npo[i].attr("worldMatrix"), self.div_roll_npo[i - 1].attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") mul_node = node.createMulNode(div_node + ".output", dm_node + ".outputTranslate") pm.connectAttr(mul_node + ".output", self.fk_npo[i].attr("t")) else: pass if i == len(self.guide.apos) - 1: # pm.connectAttr(dm_node + ".outputRotate", self.fk_local_npo2.attr("r")) _ = pm.parentConstraint(self.ik_ctl[-1], self.fk_local_npo2, skipTranslate=("x", "y", "z"), maintainOffset=True) else: pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) # self.addOperatorsOrientationLock(i, cns) self.fk_local_npo[i].setMatrix(tmp_local_npo_transform, worldSpace=True) # References if i < (len(self.fk_ctl) - 1): if self.negate: aim = (0., 1., 0.) upv = (0., 0., 1.) else: aim = (0., -1., 0.) upv = (0., 0., -1.) pm.aimConstraint(self.div_cns_npo[i + 1], self.div_cns_npo[i], mo=True, worldUpType="object", worldUpObject=self.fk_upvectors[i], worldUpVector=(0., 1., 0.), aimVector=aim, upVector=upv, )
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Curves ------------------------------------------- op = applyop.gear_curveslide2_op( self.slv_crv, self.mst_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.lenght_att, op + ".maxstretch") op = applyop.gear_curveslide2_op( self.slv_upv_crv, self.upv_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.lenght_att, op + ".maxstretch") for tang in self.tangentsCtl: for shp in tang.getShapes(): pm.connectAttr(self.tangentsVis_att, shp.attr("visibility")) for twnpo, fkctl in zip(self.tweak_npo, self.fk_ctl): intMatrix = applyop.gear_intmatrix_op( fkctl.attr("worldMatrix"), fkctl.getParent().attr("worldMatrix"), .5) applyop.gear_mulmatrix_op(intMatrix.attr("output"), twnpo.attr("parentInverseMatrix[0]"), twnpo) dm_node_scl = node.createDecomposeMatrixNode(self.root.worldMatrix) if self.settings["keepLength"]: arclen_node = pm.arclen(self.slv_crv, ch=True) alAttr = pm.getAttr(arclen_node + ".arcLength") pm.addAttr(self.slv_crv, ln="length_ratio", k=True, w=True) node.createDivNode(arclen_node.arcLength, alAttr, self.slv_crv.length_ratio) div_node_scl = node.createDivNode(self.slv_crv.length_ratio, dm_node_scl.outputScaleX) step = 1.000 / (self.def_number - 1) u = 0.000 for i in range(self.def_number): mult_node = node.createMulNode(u, self.lenght_att) cnsUpv = applyop.pathCns(self.upv_cns[i], self.slv_upv_crv, cnsType=False, u=u, tangent=False) pm.connectAttr(mult_node.outputX, cnsUpv.uValue) cns = applyop.pathCns( self.div_cns[i], self.slv_crv, False, u, True) pm.connectAttr(mult_node.outputX, cns.uValue) # Connectiong the scale for scaling compensation for axis, AX in zip("xyz", "XYZ"): pm.connectAttr(dm_node_scl.attr("outputScale{}".format(AX)), self.div_cns[i].attr("s{}".format(axis))) if self.settings["keepLength"]: div_node2 = node.createDivNode(u, div_node_scl.outputX) cond_node = node.createConditionNode(div_node2.input1X, div_node2.outputX, 4, div_node2.input1X, div_node2.outputX) # pm.connectAttr(cond_node + ".outColorR", # cnsUpv + ".uValue") # pm.connectAttr(cond_node + ".outColorR", # cns + ".uValue") pm.connectAttr(cond_node + ".outColorR", mult_node + ".input1X", f=True) # Connect the scaling for self.Extra_tweak_npo et_npo = self.Extra_tweak_npo[i] pm.connectAttr(self.spin_att, et_npo + ".rz") base_node = node.createMulNode(self.baseSize_att, 1.00000 - u, output=None) tip_node = node.createMulNode(self.tipSize_att, u, output=None) sum_node = node.createPlusMinusAverage1D([base_node.outputX, tip_node.outputX]) # print et_npo pm.connectAttr(sum_node.output1D, et_npo.scaleX, f=True) pm.connectAttr(sum_node.output1D, et_npo.scaleY, f=True) pm.connectAttr(sum_node.output1D, et_npo.scaleZ, f=True) cns.setAttr("worldUpType", 1) cns.setAttr("frontAxis", 0) cns.setAttr("upAxis", 1) pm.connectAttr(self.upv_cns[i].attr("worldMatrix[0]"), cns.attr("worldUpMatrix")) u += step for et in self.Extra_tweak_ctl: for shp in et.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility")) if self.settings["keepLength"]: # add the safty distance offset self.tweakTip_npo.attr("tx").set(self.off_dist) # connect vis line ref for shp in self.line_ref.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) for ctl in self.tweak_ctl: for shp in ctl.getShapes(): pm.connectAttr(self.ikVis_att, shp.attr("visibility")) for ctl in self.fk_ctl: for shp in ctl.getShapes(): pm.connectAttr(self.fkVis_att, shp.attr("visibility"))
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Curves ------------------------------------------- op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.maxstretch_att, op + ".maxstretch") pm.connectAttr(self.maxsquash_att, op + ".maxsquash") pm.connectAttr(self.softness_att, op + ".softness") # Division ----------------------------------------- rootWorld_node = node.createDecomposeMatrixNode( self.root.attr("worldMatrix")) for i in range(self.settings["fkNb"]): # References u = i / (self.settings["fkNb"] - 1.0) if i == 0: # we add extra 10% to the first position u = (1.0 / (self.settings["fkNb"] - 1.0)) / 10 cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 0) # front axis is 'X' cns.setAttr("upAxis", 2) # front axis is 'Z' # Roll intMatrix = applyop.gear_intmatrix_op( self.ik_ctl[0] + ".worldMatrix", self.ik_ctl[-1] + ".worldMatrix", u) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") # compensate scale reference div_node = node.createDivNode([1, 1, 1], [ rootWorld_node + ".outputScaleX", rootWorld_node + ".outputScaleY", rootWorld_node + ".outputScaleZ" ]) # Controlers if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) else: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.div_cns[i - 1].attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") mul_node = node.createMulNode(div_node + ".output", dm_node + ".outputTranslate") pm.connectAttr(mul_node + ".output", self.fk_npo[i].attr("t")) pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) # Orientation Lock if i == 0: dm_node = node.createDecomposeMatrixNode(self.ik_ctl[0] + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], 0) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") elif i == self.settings["fkNb"] - 1: dm_node = node.createDecomposeMatrixNode(self.ik_ctl[-1] + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], 0) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate")
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Tangent position --------------------------------- # common part d = vector.getDistance(self.guide.apos[0], self.guide.apos[1]) dist_node = node.createDistNode(self.ik0_ctl, self.ik1_ctl) rootWorld_node = node.createDecomposeMatrixNode( self.root.attr("worldMatrix")) div_node = node.createDivNode(dist_node + ".distance", rootWorld_node + ".outputScaleX") div_node = node.createDivNode(div_node + ".outputX", d) # tan0 mul_node = node.createMulNode(self.tan0_att, self.tan0_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan0_npo.attr("ty")) # tan1 mul_node = node.createMulNode(self.tan1_att, self.tan1_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan1_npo.attr("ty")) # Tangent Mid -------------------------------------- if self.settings["centralTangent"]: tanIntMat = applyop.gear_intmatrix_op( self.tan0_npo.attr("worldMatrix"), self.tan1_npo.attr("worldMatrix"), .5) applyop.gear_mulmatrix_op( tanIntMat.attr("output"), self.tan_npo.attr("parentInverseMatrix[0]"), self.tan_npo) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan0_off.attr("translate")) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan1_off.attr("translate")) # Curves ------------------------------------------- op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.maxstretch_att, op + ".maxstretch") pm.connectAttr(self.maxsquash_att, op + ".maxsquash") pm.connectAttr(self.softness_att, op + ".softness") # Volume driver ------------------------------------ crv_node = node.createCurveInfoNode(self.slv_crv) # Division ----------------------------------------- for i in range(self.settings["division"]): # References u = i / (self.settings["division"] - 1.0) cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 0) # front axis is 'X' # Roll intMatrix = applyop.gear_intmatrix_op( self.ik0_ctl + ".worldMatrix", self.ik1_ctl + ".worldMatrix", u) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") # Squash n Stretch op = applyop.gear_squashstretch2_op(self.scl_transforms[i], self.root, pm.arclen(self.slv_crv), "y") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") pm.connectAttr(self.st_att[i], op + ".stretch") pm.connectAttr(self.sq_att[i], op + ".squash") # Connections (Hooks) ------------------------------ pm.pointConstraint(self.scl_transforms[0], self.cnx0) pm.scaleConstraint(self.scl_transforms[0], self.cnx0) pm.orientConstraint(self.ik0_ctl, self.cnx0) pm.pointConstraint(self.scl_transforms[-1], self.cnx1) pm.scaleConstraint(self.scl_transforms[-1], self.cnx1) pm.orientConstraint(self.ik1_ctl, self.cnx1)
def rig( eyeMesh=None, edgeLoop="", blinkH=20, namePrefix="eye", offset=0.05, rigidLoops=2, falloffLoops=4, headJnt=None, doSkin=True, parent_node=None, ctlName="ctl", sideRange=False, customCorner=False, intCorner=None, extCorner=None, ctlSet=None, defSet=None, upperVTrack=0.02, upperHTrack=0.01, lowerVTrack=0.02, lowerHTrack=0.01, aim_controller="", deformers_group="", everyNVertex=1, ): """Create eyelid and eye rig Args: eyeMesh (TYPE): Description edgeLoop (TYPE): Description blinkH (TYPE): Description namePrefix (TYPE): Description offset (TYPE): Description rigidLoops (TYPE): Description falloffLoops (TYPE): Description headJnt (TYPE): Description doSkin (TYPE): Description parent_node (None, optional): Description ctlName (str, optional): Description sideRange (bool, optional): Description customCorner (bool, optional): Description intCorner (None, optional): Description extCorner (None, optional): Description ctlSet (None, optional): Description defSet (None, optional): Description upperVTrack (None, optional): Description upperHTrack (None, optional): Description lowerVTrack (None, optional): Description lowerHTrack (None, optional): Description aim_controller (None, optional): Description deformers_group (None, optional): Description everyNVertex (int, optional): Will create a joint every N vertex No Longer Returned: TYPE: Description """ ########################################## # INITIAL SETUP ########################################## up_axis = pm.upAxis(q=True, axis=True) if up_axis == "z": z_up = True else: z_up = False # getters edgeLoopList = get_edge_loop(edgeLoop) eyeMesh = get_eye_mesh(eyeMesh) # checkers if not edgeLoopList or not eyeMesh: return if doSkin: if not headJnt: pm.displayWarning("Please set the Head Jnt or unCheck " "Compute Topological Autoskin") return # Convert data blinkH = blinkH / 100.0 # Initial Data bboxCenter = meshNavigation.bboxCenter(eyeMesh) extr_v = meshNavigation.getExtremeVertexFromLoop(edgeLoopList, sideRange, z_up) upPos = extr_v[0] lowPos = extr_v[1] inPos = extr_v[2] outPos = extr_v[3] edgeList = extr_v[4] vertexList = extr_v[5] # Detect the side L or R from the x value if inPos.getPosition(space="world")[0] < 0.0: side = "R" inPos = extr_v[3] outPos = extr_v[2] normalPos = outPos npw = normalPos.getPosition(space="world") normalVec = npw - bboxCenter else: side = "L" normalPos = outPos npw = normalPos.getPosition(space="world") normalVec = bboxCenter - npw # Manual Vertex corners if customCorner: if intCorner: try: if side == "R": inPos = pm.PyNode(extCorner) else: inPos = pm.PyNode(intCorner) except pm.MayaNodeError: pm.displayWarning("%s can not be found" % intCorner) return else: pm.displayWarning("Please set the internal eyelid corner") return if extCorner: try: normalPos = pm.PyNode(extCorner) npw = normalPos.getPosition(space="world") if side == "R": outPos = pm.PyNode(intCorner) normalVec = npw - bboxCenter else: outPos = pm.PyNode(extCorner) normalVec = bboxCenter - npw except pm.MayaNodeError: pm.displayWarning("%s can not be found" % extCorner) return else: pm.displayWarning("Please set the external eyelid corner") return # Check if we have prefix: if namePrefix: namePrefix = string.removeInvalidCharacter(namePrefix) else: pm.displayWarning("Prefix is needed") return def setName(name, ind=None): namesList = [namePrefix, side, name] if ind is not None: namesList[1] = side + str(ind) name = "_".join(namesList) return name if pm.ls(setName("root")): pm.displayWarning("The object %s already exist in the scene. Please " "choose another name prefix" % setName("root")) return ########################################## # CREATE OBJECTS ########################################## # Eye root eye_root = primitive.addTransform(None, setName("root")) eyeCrv_root = primitive.addTransform(eye_root, setName("crvs")) # Eyelid Main crvs try: upEyelid_edge = meshNavigation.edgeRangeInLoopFromMid( edgeList, upPos, inPos, outPos) up_crv = curve.createCurveFromOrderedEdges(upEyelid_edge, inPos, setName("upperEyelid"), parent=eyeCrv_root) upCtl_crv = curve.createCurveFromOrderedEdges(upEyelid_edge, inPos, setName("upCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(upCtl_crv, s=2, rt=0, rpo=True, ch=False) lowEyelid_edge = meshNavigation.edgeRangeInLoopFromMid( edgeList, lowPos, inPos, outPos) low_crv = curve.createCurveFromOrderedEdges(lowEyelid_edge, inPos, setName("lowerEyelid"), parent=eyeCrv_root) lowCtl_crv = curve.createCurveFromOrderedEdges(lowEyelid_edge, inPos, setName("lowCtl_crv"), parent=eyeCrv_root) pm.rebuildCurve(lowCtl_crv, s=2, rt=0, rpo=True, ch=False) except UnboundLocalError: if customCorner: pm.displayWarning("This error is maybe caused because the custom " "Corner vertex is not part of the edge loop") pm.displayError(traceback.format_exc()) return # blendshape curves. All crv have 30 point to allow blendshape connect upDriver_crv = curve.createCurveFromCurve(up_crv, setName("upDriver_crv"), nbPoints=30, parent=eyeCrv_root) upDriver_crv.attr("lineWidth").set(5) lowDriver_crv = curve.createCurveFromCurve(low_crv, setName("lowDriver_crv"), nbPoints=30, parent=eyeCrv_root) lowDriver_crv.attr("lineWidth").set(5) upRest_target_crv = curve.createCurveFromCurve( up_crv, setName("upRest_target_crv"), nbPoints=30, parent=eyeCrv_root) lowRest_target_crv = curve.createCurveFromCurve( low_crv, setName("lowRest_target_crv"), nbPoints=30, parent=eyeCrv_root) upProfile_target_crv = curve.createCurveFromCurve( up_crv, setName("upProfile_target_crv"), nbPoints=30, parent=eyeCrv_root, ) lowProfile_target_crv = curve.createCurveFromCurve( low_crv, setName("lowProfile_target_crv"), nbPoints=30, parent=eyeCrv_root, ) # mid driver midUpDriver_crv = curve.createCurveFromCurve(up_crv, setName("midUpDriver_crv"), nbPoints=30, parent=eyeCrv_root) midLowDriver_crv = curve.createCurveFromCurve(low_crv, setName("midLowDriver_crv"), nbPoints=30, parent=eyeCrv_root) # curve that define the close point of the eyelid closeTarget_crv = curve.createCurveFromCurve(up_crv, setName("closeTarget_crv"), nbPoints=30, parent=eyeCrv_root) eyeCrv_root.attr("visibility").set(False) # localBBOX localBBox = eyeMesh.getBoundingBox(invisible=True, space="world") wRadius = abs((localBBox[0][0] - localBBox[1][0])) dRadius = abs((localBBox[0][1] - localBBox[1][1]) / 1.7) # Groups if not ctlSet: ctlSet = "rig_controllers_grp" try: ctlSet = pm.PyNode(ctlSet) except pm.MayaNodeError: pm.sets(n=ctlSet, em=True) ctlSet = pm.PyNode(ctlSet) if not defSet: defSet = "rig_deformers_grp" try: defset = pm.PyNode(defSet) except pm.MayaNodeError: pm.sets(n=defSet, em=True) defset = pm.PyNode(defSet) # Calculate center looking at averagePosition = (upPos.getPosition(space="world") + lowPos.getPosition( space="world") + inPos.getPosition(space="world") + outPos.getPosition(space="world")) / 4 if z_up: axis = "zx" else: axis = "z-x" t = transform.getTransformLookingAt(bboxCenter, averagePosition, normalVec, axis=axis, negate=False) over_npo = primitive.addTransform(eye_root, setName("center_lookatRoot"), t) center_lookat = primitive.addTransform(over_npo, setName("center_lookat"), t) if side == "R": over_npo.attr("rx").set(over_npo.attr("rx").get() * -1) over_npo.attr("ry").set(over_npo.attr("ry").get() + 180) over_npo.attr("sz").set(-1) t = transform.getTransform(over_npo) # Tracking # Eye aim control t_arrow = transform.getTransformLookingAt( bboxCenter, averagePosition, upPos.getPosition(space="world"), axis="zy", negate=False, ) radius = abs((localBBox[0][0] - localBBox[1][0]) / 1.7) arrow_ctl = None arrow_npo = None if aim_controller: arrow_ctl = pm.PyNode(aim_controller) else: arrow_npo = primitive.addTransform(eye_root, setName("aim_npo"), t_arrow) arrow_ctl = icon.create( arrow_npo, setName("aim_%s" % ctlName), t_arrow, icon="arrow", w=1, po=datatypes.Vector(0, 0, radius), color=4, ) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=arrow_ctl) attribute.setKeyableAttributes(arrow_ctl, params=["rx", "ry", "rz"]) attribute.addAttribute(arrow_ctl, "isCtl", "bool", keyable=False) # tracking custom trigger if side == "R": tt = t_arrow else: tt = t aimTrigger_root = primitive.addTransform(center_lookat, setName("aimTrigger_root"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_root) aimTrigger_lvl = primitive.addTransform(aimTrigger_root, setName("aimTrigger_lvl"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_lvl) aimTrigger_lvl.attr("tz").set(1.0) aimTrigger_ref = primitive.addTransform(aimTrigger_lvl, setName("aimTrigger_ref"), tt) # For some unknown reason the right side gets scewed rotation values mgear.core.transform.resetTransform(aimTrigger_ref) aimTrigger_ref.attr("tz").set(0.0) # connect trigger with arrow_ctl pm.parentConstraint(arrow_ctl, aimTrigger_ref, mo=True) # Blink driver controls if z_up: trigger_axis = "tz" ro_up = [0, 1.57079633 * 2, 1.57079633] ro_low = [0, 0, 1.57079633] po = [0, offset * -1, 0] low_pos = 2 # Z else: trigger_axis = "ty" ro_up = (1.57079633, 1.57079633, 0) ro_low = [1.57079633, 1.57079633, 1.57079633 * 2] po = [0, 0, offset] low_pos = 1 # Y # upper ctl p = upRest_target_crv.getCVs(space="world")[15] ut = transform.setMatrixPosition(datatypes.Matrix(), p) npo = primitive.addTransform(over_npo, setName("upBlink_npo"), ut) up_ctl = icon.create( npo, setName("upBlink_ctl"), ut, icon="arrow", w=2.5, d=2.5, ro=datatypes.Vector(ro_up[0], ro_up[1], ro_up[2]), po=datatypes.Vector(po[0], po[1], po[2]), color=4, ) attribute.setKeyableAttributes(up_ctl, [trigger_axis]) pm.sets(ctlSet, add=up_ctl) # use translation of the object to drive the blink blink_driver = primitive.addTransform(up_ctl, setName("blink_drv"), ut) # lowe ctl p_low = lowRest_target_crv.getCVs(space="world")[15] p[low_pos] = p_low[low_pos] lt = transform.setMatrixPosition(ut, p) npo = primitive.addTransform(over_npo, setName("lowBlink_npo"), lt) low_ctl = icon.create( npo, setName("lowBlink_ctl"), lt, icon="arrow", w=1.5, d=1.5, ro=datatypes.Vector(ro_low[0], ro_low[1], ro_low[2]), po=datatypes.Vector(po[0], po[1], po[2]), color=4, ) attribute.setKeyableAttributes(low_ctl, [trigger_axis]) pm.sets(ctlSet, add=low_ctl) # Controls lists upControls = [] trackLvl = [] track_corner_lvl = [] corner_ctl = [] ghost_ctl = [] # upper eyelid controls upperCtlNames = ["inCorner", "upInMid", "upMid", "upOutMid", "outCorner"] cvs = upCtl_crv.getCVs(space="world") if side == "R" and not sideRange: # if side == "R": cvs = [cv for cv in reversed(cvs)] # offset = offset * -1 for i, cv in enumerate(cvs): if utils.is_odd(i): color = 14 wd = 0.3 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = 0.6 icon_shape = "circle" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz", ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % upperCtlNames[i]), t) npoBase = npo # track for corners and mid point level if i in [0, 2, 4]: # we add an extra level to input the tracking ofset values npo = primitive.addTransform(npo, setName("%s_trk" % upperCtlNames[i]), t) if i == 2: trackLvl.append(npo) else: track_corner_lvl.append(npo) if i in [1, 2, 3]: ctl = primitive.addTransform(npo, setName("%s_loc" % upperCtlNames[i]), t) # ghost controls if i == 2: gt = transform.setMatrixPosition( t, transform.getPositionFromMatrix(ut)) else: gt = t npo_g = primitive.addTransform( up_ctl, setName("%sCtl_npo" % upperCtlNames[i]), gt) ctl_g = icon.create( npo_g, setName("%s_%s" % (upperCtlNames[i], ctlName)), gt, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl_g ghost_ctl.append(ctl_g) # connect local SRT rigbits.connectLocalTransform([ctl_g, ctl]) else: ctl = icon.create( npo, setName("%s_%s" % (upperCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl attribute.addAttribute(ctl_param, "isCtl", "bool", keyable=False) attribute.add_mirror_config_channels(ctl_param) node.add_controller_tag(ctl_param, over_npo) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl_param) attribute.setKeyableAttributes(ctl_param, params) upControls.append(ctl) # add corner ctls to corner ctl list for tracking if i in [0, 4]: corner_ctl.append(ctl) # if side == "R": # npoBase.attr("ry").set(180) # npoBase.attr("sz").set(-1) # adding parent constraints to odd controls for i, ctl in enumerate(upControls): if utils.is_odd(i): cns_node = pm.parentConstraint(upControls[i - 1], upControls[i + 1], ctl.getParent(), mo=True) # Make the constraint "noFlip" cns_node.interpType.set(0) # adding parent constraint ghost controls cns_node = pm.parentConstraint(ghost_ctl[1], upControls[0], ghost_ctl[0].getParent(), mo=True) cns_node.interpType.set(0) cns_node = pm.parentConstraint(ghost_ctl[1], upControls[-1], ghost_ctl[2].getParent(), mo=True) cns_node.interpType.set(0) # lower eyelid controls lowControls = [upControls[0]] lowerCtlNames = [ "inCorner", "lowInMid", "lowMid", "lowOutMid", "outCorner", ] cvs = lowCtl_crv.getCVs(space="world") if side == "R" and not sideRange: cvs = [cv for cv in reversed(cvs)] for i, cv in enumerate(cvs): # we skip the first and last point since is already in the uper eyelid if i in [0, 4]: continue if utils.is_odd(i): color = 14 wd = 0.3 icon_shape = "circle" params = ["tx", "ty", "tz"] else: color = 4 wd = 0.6 icon_shape = "circle" params = [ "tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz", ] t = transform.setMatrixPosition(t, cvs[i]) npo = primitive.addTransform(center_lookat, setName("%s_npo" % lowerCtlNames[i]), t) npoBase = npo if i in [1, 2, 3]: if i == 2: # we add an extra level to input the tracking ofset values npo = primitive.addTransform( npo, setName("%s_trk" % lowerCtlNames[i]), t) trackLvl.append(npo) ctl = primitive.addTransform(npo, setName("%s_loc" % lowerCtlNames[i]), t) # ghost controls if i == 2: gt = transform.setMatrixPosition( t, transform.getPositionFromMatrix(lt)) else: gt = t # ghost controls npo_g = primitive.addTransform( low_ctl, setName("%sCtl_npo" % lowerCtlNames[i]), gt) ctl_g = icon.create( npo_g, setName("%s_%s" % (lowerCtlNames[i], ctlName)), gt, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl_g ghost_ctl.append(ctl_g) # connect local SRT rigbits.connectLocalTransform([ctl_g, ctl]) else: ctl = icon.create( npo, setName("%s_%s" % (lowerCtlNames[i], ctlName)), t, icon=icon_shape, w=wd, d=wd, ro=datatypes.Vector(1.57079633, 0, 0), po=datatypes.Vector(0, 0, offset), color=color, ) # define the ctl_param to recive the ctl configuration ctl_param = ctl attribute.addAttribute(ctl_param, "isCtl", "bool", keyable=False) attribute.add_mirror_config_channels(ctl_param) lowControls.append(ctl) if len(ctlName.split("_")) == 2 and ctlName.split("_")[-1] == "ghost": pass else: pm.sets(ctlSet, add=ctl_param) attribute.setKeyableAttributes(ctl_param, params) # mirror behaviout on R side controls # if side == "R": # npoBase.attr("ry").set(180) # npoBase.attr("sz").set(-1) for lctl in reversed(lowControls[1:]): node.add_controller_tag(lctl, over_npo) lowControls.append(upControls[-1]) # adding parent constraints to odd controls for i, ctl in enumerate(lowControls): if utils.is_odd(i): cns_node = pm.parentConstraint( lowControls[i - 1], lowControls[i + 1], ctl.getParent(), mo=True, ) # Make the constraint "noFlip" cns_node.interpType.set(0) # adding parent constraint ghost controls cns_node = pm.parentConstraint(ghost_ctl[4], upControls[0], ghost_ctl[3].getParent(), mo=True) cns_node.interpType.set(0) cns_node = pm.parentConstraint(ghost_ctl[4], upControls[-1], ghost_ctl[5].getParent(), mo=True) cns_node.interpType.set(0) ########################################## # OPERATORS ########################################## # Connecting control crvs with controls applyop.gear_curvecns_op(upCtl_crv, upControls) applyop.gear_curvecns_op(lowCtl_crv, lowControls) # adding wires w1 = pm.wire(up_crv, w=upDriver_crv)[0] w2 = pm.wire(low_crv, w=lowDriver_crv)[0] w3 = pm.wire(upProfile_target_crv, w=upCtl_crv)[0] w4 = pm.wire(lowProfile_target_crv, w=lowCtl_crv)[0] if z_up: trigger_axis = "tz" else: trigger_axis = "ty" # connect blink driver pm.pointConstraint(low_ctl, blink_driver, mo=False) rest_val = blink_driver.attr(trigger_axis).get() up_div_node = node.createDivNode(up_ctl.attr(trigger_axis), rest_val) low_div_node = node.createDivNode(low_ctl.attr(trigger_axis), rest_val * -1) # contact driver minus_node = node.createPlusMinusAverage1D( [rest_val, blink_driver.attr(trigger_axis)], operation=2) contact_div_node = node.createDivNode(minus_node.output1D, rest_val) # wire tension for w in [w1, w2, w3, w4]: w.dropoffDistance[0].set(100) # TODO: what is the best solution? # trigger using proximity # remap_node = pm.createNode("remapValue") # contact_div_node.outputX >> remap_node.inputValue # remap_node.value[0].value_Interp.set(2) # remap_node.inputMin.set(0.995) # reverse_node = node.createReverseNode(remap_node.outColorR) # for w in [w1, w2]: # reverse_node.outputX >> w.scale[0] # trigger at starting movement for up and low # up remap_node = pm.createNode("remapValue") up_ctl.attr(trigger_axis) >> remap_node.inputValue remap_node.value[0].value_Interp.set(2) remap_node.inputMax.set(rest_val / 8) reverse_node = node.createReverseNode(remap_node.outColorR) reverse_node.outputX >> w1.scale[0] # low remap_node = pm.createNode("remapValue") low_ctl.attr(trigger_axis) >> remap_node.inputValue remap_node.value[0].value_Interp.set(2) remap_node.inputMin.set((rest_val / 8) * -1) remap_node.outColorR >> w2.scale[0] # mid position drivers blendshapes bs_midUpDrive = pm.blendShape( lowRest_target_crv, upProfile_target_crv, midUpDriver_crv, n="midUpDriver_blendShape", ) bs_midLowDrive = pm.blendShape( upRest_target_crv, lowProfile_target_crv, midLowDriver_crv, n="midlowDriver_blendShape", ) bs_closeTarget = pm.blendShape( midUpDriver_crv, midLowDriver_crv, closeTarget_crv, n="closeTarget_blendShape", ) pm.connectAttr( up_div_node.outputX, bs_midUpDrive[0].attr(lowRest_target_crv.name()), ) pm.connectAttr( low_div_node.outputX, bs_midLowDrive[0].attr(upRest_target_crv.name()), ) pm.setAttr(bs_closeTarget[0].attr(midUpDriver_crv.name()), 0.5) pm.setAttr(bs_closeTarget[0].attr(midLowDriver_crv.name()), 0.5) # Main crv drivers bs_upBlink = pm.blendShape( lowRest_target_crv, closeTarget_crv, upProfile_target_crv, upDriver_crv, n="upBlink_blendShape", ) bs_lowBlink = pm.blendShape( upRest_target_crv, closeTarget_crv, lowProfile_target_crv, lowDriver_crv, n="lowBlink_blendShape", ) # blink contact connections cond_node_up = node.createConditionNode(contact_div_node.outputX, 1, 3, 0, up_div_node.outputX) pm.connectAttr( cond_node_up.outColorR, bs_upBlink[0].attr(lowRest_target_crv.name()), ) cond_node_low = node.createConditionNode(contact_div_node.outputX, 1, 3, 0, low_div_node.outputX) pm.connectAttr( cond_node_low.outColorR, bs_lowBlink[0].attr(upRest_target_crv.name()), ) cond_node_close = node.createConditionNode(contact_div_node.outputX, 1, 2, 1, 0) cond_node_close.colorIfFalseR.set(0) pm.connectAttr( cond_node_close.outColorR, bs_upBlink[0].attr(closeTarget_crv.name()), ) pm.connectAttr( cond_node_close.outColorR, bs_lowBlink[0].attr(closeTarget_crv.name()), ) pm.setAttr(bs_upBlink[0].attr(upProfile_target_crv.name()), 1) pm.setAttr(bs_lowBlink[0].attr(lowProfile_target_crv.name()), 1) # joints root jnt_root = primitive.addTransformFromPos(eye_root, setName("joints"), pos=bboxCenter) if deformers_group: deformers_group = pm.PyNode(deformers_group) pm.parentConstraint(eye_root, jnt_root, mo=True) pm.scaleConstraint(eye_root, jnt_root, mo=True) deformers_group.addChild(jnt_root) # head joint if headJnt: try: headJnt = pm.PyNode(headJnt) jnt_base = headJnt except pm.MayaNodeError: pm.displayWarning("Aborted can not find %s " % headJnt) return else: # Eye root jnt_base = jnt_root eyeTargets_root = primitive.addTransform(eye_root, setName("targets")) eyeCenter_jnt = rigbits.addJnt(arrow_ctl, jnt_base, grp=defset, jntName=setName("center_jnt")) # Upper Eyelid joints ################################################## cvs = up_crv.getCVs(space="world") upCrv_info = node.createCurveInfoNode(up_crv) # aim constrain targets and joints upperEyelid_aimTargets = [] upperEyelid_jnt = [] upperEyelid_jntRoot = [] if z_up: axis = "zy" wupVector = [0, 0, 1] else: axis = "-yz" wupVector = [0, 1, 0] for i, cv in enumerate(cvs): if i % everyNVertex: continue # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("upEyelid_aimTarget", i), pos=cv) upperEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(upCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("upEyelid_jnt_base", i), pos=bboxCenter) jntRoot.attr("radius").set(0.08) jntRoot.attr("visibility").set(False) upperEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis=axis, wupObject=jnt_root, wupVector=wupVector) jnt_ref = primitive.addJointFromPos(jntRoot, setName("upEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(0.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("upEyelid_jnt", i)) upperEyelid_jnt.append(jnt) # Lower Eyelid joints ################################################## cvs = low_crv.getCVs(space="world") lowCrv_info = node.createCurveInfoNode(low_crv) # aim constrain targets and joints lowerEyelid_aimTargets = [] lowerEyelid_jnt = [] lowerEyelid_jntRoot = [] for i, cv in enumerate(cvs): if i in [0, len(cvs) - 1]: continue if i % everyNVertex: continue # aim targets trn = primitive.addTransformFromPos(eyeTargets_root, setName("lowEyelid_aimTarget", i), pos=cv) lowerEyelid_aimTargets.append(trn) # connecting positions with crv pm.connectAttr(lowCrv_info + ".controlPoints[%s]" % str(i), trn.attr("translate")) # joints jntRoot = primitive.addJointFromPos(jnt_root, setName("lowEyelid_base", i), pos=bboxCenter) jntRoot.attr("radius").set(0.08) jntRoot.attr("visibility").set(False) lowerEyelid_jntRoot.append(jntRoot) applyop.aimCns(jntRoot, trn, axis=axis, wupObject=jnt_root, wupVector=wupVector) jnt_ref = primitive.addJointFromPos(jntRoot, setName("lowEyelid_jnt_ref", i), pos=cv) jnt_ref.attr("radius").set(0.08) jnt_ref.attr("visibility").set(False) jnt = rigbits.addJnt(jnt_ref, jnt_base, grp=defset, jntName=setName("lowEyelid_jnt", i)) lowerEyelid_jnt.append(jnt) # Adding channels for eye tracking upVTracking_att = attribute.addAttribute(up_ctl, "vTracking", "float", upperVTrack, minValue=0) upHTracking_att = attribute.addAttribute(up_ctl, "hTracking", "float", upperHTrack, minValue=0) lowVTracking_att = attribute.addAttribute(low_ctl, "vTracking", "float", lowerVTrack, minValue=0) lowHTracking_att = attribute.addAttribute(low_ctl, "hTracking", "float", lowerHTrack, minValue=0) # vertical tracking connect up_mult_node = node.createMulNode(upVTracking_att, aimTrigger_ref.attr("ty")) low_mult_node = node.createMulNode(lowVTracking_att, aimTrigger_ref.attr("ty")) # remap to use the low or the up eyelid as driver contact base on # the eyetrack trigger direction uT_remap_node = pm.createNode("remapValue") aimTrigger_ref.attr("ty") >> uT_remap_node.inputValue uT_remap_node.inputMax.set(0.1) uT_remap_node.inputMin.set(-0.1) up_mult_node.outputX >> uT_remap_node.outputMax low_mult_node.outputX >> uT_remap_node.outputMin # up u_remap_node = pm.createNode("remapValue") contact_div_node.outputX >> u_remap_node.inputValue u_remap_node.value[0].value_Interp.set(2) u_remap_node.inputMin.set(0.9) up_mult_node.outputX >> u_remap_node.outputMin uT_remap_node.outColorR >> u_remap_node.outputMax # low l_remap_node = pm.createNode("remapValue") contact_div_node.outputX >> l_remap_node.inputValue l_remap_node.value[0].value_Interp.set(2) l_remap_node.inputMin.set(0.9) low_mult_node.outputX >> l_remap_node.outputMin uT_remap_node.outColorR >> l_remap_node.outputMax # up connect and turn down to low when contact pm.connectAttr(u_remap_node.outColorR, trackLvl[0].attr("ty")) pm.connectAttr(l_remap_node.outColorR, trackLvl[1].attr("ty")) # horizontal tracking connect mult_node = node.createMulNode(upHTracking_att, aimTrigger_ref.attr("tx")) # Correct right side horizontal tracking # if side == "R": # mult_node = node.createMulNode(mult_node.attr("outputX"), -1) pm.connectAttr(mult_node + ".outputX", trackLvl[0].attr("tx")) mult_node = node.createMulNode(lowHTracking_att, aimTrigger_ref.attr("tx")) # Correct right side horizontal tracking # if side == "R": # mult_node = node.createMulNode(mult_node.attr("outputX"), -1) pm.connectAttr(mult_node + ".outputX", trackLvl[1].attr("tx")) # adding channels for corner tracking # track_corner_lvl for i, ctl in enumerate(corner_ctl): VTracking_att = attribute.addAttribute(ctl, "vTracking", "float", 0.1, minValue=0) if z_up: mult_node = node.createMulNode(VTracking_att, up_ctl.tz) mult_node2 = node.createMulNode(VTracking_att, low_ctl.tz) plus_node = node.createPlusMinusAverage1D( [mult_node.outputX, mult_node2.outputX]) mult_node3 = node.createMulNode(plus_node.output1D, -1) pm.connectAttr(mult_node3.outputX, track_corner_lvl[i].attr("ty")) else: mult_node = node.createMulNode(VTracking_att, up_ctl.ty) mult_node2 = node.createMulNode(VTracking_att, low_ctl.ty) plus_node = node.createPlusMinusAverage1D( [mult_node.outputX, mult_node2.outputX]) pm.connectAttr(plus_node.output1D, track_corner_lvl[i].attr("ty")) ########################################### # Reparenting ########################################### if parent_node: try: if isinstance(parent_node, string_types): parent_node = pm.PyNode(parent_node) parent_node.addChild(eye_root) except pm.MayaNodeError: pm.displayWarning("The eye rig can not be parent to: %s. Maybe " "this object doesn't exist." % parent_node) ########################################### # Auto Skinning ########################################### if doSkin: # eyelid vertex rows totalLoops = rigidLoops + falloffLoops vertexLoopList = meshNavigation.getConcentricVertexLoop( vertexList, totalLoops) vertexRowList = meshNavigation.getVertexRowsFromLoops(vertexLoopList) # we set the first value 100% for the first initial loop skinPercList = [1.0] # we expect to have a regular grid topology for r in range(rigidLoops): for rr in range(2): skinPercList.append(1.0) increment = 1.0 / float(falloffLoops) # we invert to smooth out from 100 to 0 inv = 1.0 - increment for r in range(falloffLoops): for rr in range(2): if inv < 0.0: inv = 0.0 skinPercList.append(inv) inv -= increment # this loop add an extra 0.0 indices to avoid errors for r in range(10): for rr in range(2): skinPercList.append(0.0) # base skin geo = pm.listRelatives(edgeLoopList[0], parent=True)[0] # Check if the object has a skinCluster objName = pm.listRelatives(geo, parent=True)[0] skinCluster = skin.getSkinCluster(objName) if not skinCluster: skinCluster = pm.skinCluster(headJnt, geo, tsb=True, nw=2, n="skinClsEyelid") eyelidJoints = upperEyelid_jnt + lowerEyelid_jnt pm.progressWindow(title="Auto skinning process", progress=0, max=len(eyelidJoints)) firstBoundary = False for jnt in eyelidJoints: pm.progressWindow(e=True, step=1, status="\nSkinning %s" % jnt) skinCluster.addInfluence(jnt, weight=0) v = meshNavigation.getClosestVertexFromTransform(geo, jnt) for row in vertexRowList: if v in row: it = 0 # iterator inc = 1 # increment for i, rv in enumerate(row): try: perc = skinPercList[it] t_val = [(jnt, perc), (headJnt, 1.0 - perc)] pm.skinPercent(skinCluster, rv, transformValue=t_val) if rv.isOnBoundary(): # we need to compare with the first boundary # to check if the row have inverted direction # and offset the value if not firstBoundary: firstBoundary = True firstBoundaryValue = it else: if it < firstBoundaryValue: it -= 1 elif it > firstBoundaryValue: it += 1 inc = 2 except IndexError: continue it = it + inc pm.progressWindow(e=True, endProgress=True) # Eye Mesh skinning skinCluster = skin.getSkinCluster(eyeMesh) if not skinCluster: skinCluster = pm.skinCluster(eyeCenter_jnt, eyeMesh, tsb=True, nw=1, n="skinClsEye")
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Tangent position --------------------------------- # common part d = vector.getDistance(self.guide.pos["root"], self.guide.pos["neck"]) dist_node = node.createDistNode(self.root, self.ik_ctl) rootWorld_node = node.createDecomposeMatrixNode( self.root.attr("worldMatrix")) div_node = node.createDivNode(dist_node + ".distance", rootWorld_node + ".outputScaleX") div_node = node.createDivNode(div_node + ".outputX", d) # tan0 mul_node = node.createMulNode(self.tan0_att, self.tan0_loc.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan0_loc + ".ty") # tan1 mul_node = node.createMulNode(self.tan1_att, self.tan1_loc.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan1_loc.attr("ty")) # Curves ------------------------------------------- op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, 0.5, 0.5) pm.connectAttr(self.maxstretch_att, op + ".maxstretch") pm.connectAttr(self.maxsquash_att, op + ".maxsquash") pm.connectAttr(self.softness_att, op + ".softness") # Volume driver ------------------------------------ crv_node = node.createCurveInfoNode(self.slv_crv) # Division ----------------------------------------- for i in range(self.divisions): # References u = i / (self.divisions - 1.0) cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 2) # front axis is 'Z' # Roll intMatrix = applyop.gear_intmatrix_op( self.intMRef + ".worldMatrix", self.ik_ctl + ".worldMatrix", u) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") # Squash n Stretch op = applyop.gear_squashstretch2_op(self.fk_npo[i], self.root, pm.arclen(self.slv_crv), "y") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") pm.connectAttr(self.st_att[i], op + ".stretch") pm.connectAttr(self.sq_att[i], op + ".squash") op.setAttr("driver_min", 0.1) # scl compas if i != 0: div_node = node.createDivNode( [1, 1, 1], [ self.fk_npo[i - 1] + ".sx", self.fk_npo[i - 1] + ".sy", self.fk_npo[i - 1] + ".sz", ], ) pm.connectAttr(div_node + ".output", self.scl_npo[i] + ".scale") # Controlers if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix"), ) else: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.div_cns[i - 1].attr("worldInverseMatrix"), ) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) # Orientation Lock if i == self.divisions - 1: dm_node = node.createDecomposeMatrixNode(self.ik_ctl + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori_att, ) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") # Head --------------------------------------------- self.fk_ctl[-1].addChild(self.head_cns) # scale compensation dm_node = node.createDecomposeMatrixNode(self.scl_npo[0] + ".parentInverseMatrix") pm.connectAttr(dm_node + ".outputScale", self.scl_npo[0] + ".scale")
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref ============================================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleArmChainUpvRef"), self.armChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.armChainUpvRef[0], self.upv_cns, mo=True) # Visibilities ------------------------------------- # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) if self.settings["ikTR"]: for shp in self.ikRot_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # Controls ROT order ----------------------------------- attribute.setRotOrder(self.fk0_ctl, "XZY") attribute.setRotOrder(self.fk1_ctl, "XYZ") attribute.setRotOrder(self.fk2_ctl, "YZX") attribute.setRotOrder(self.ik_ctl, "XYZ") # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) if self.settings["ikTR"]: # connect the control inputs outEff_dm = o_node.listConnections(c=True)[-1][1] inAttr = self.ikRot_npo.attr("translate") outEff_dm.attr("outputTranslate") >> inAttr outEff_dm.attr("outputScale") >> self.ikRot_npo.attr("scale") dm_node = node.createDecomposeMatrixNode(o_node.attr("outB")) dm_node.attr("outputRotate") >> self.ikRot_npo.attr("rotate") # rotation mulM_node = applyop.gear_mulmatrix_op( self.ikRot_ctl.attr("worldMatrix"), self.eff_loc.attr("parentInverseMatrix")) intM_node = applyop.gear_intmatrix_op(o_node.attr("outEff"), mulM_node.attr("output"), o_node.attr("blend")) dm_node = node.createDecomposeMatrixNode(intM_node.attr("output")) dm_node.attr("outputRotate") >> self.eff_loc.attr("rotate") transform.matchWorldTransform(self.fk2_ctl, self.ikRot_cns) # scale: this fix the scalin popping issue intM_node = applyop.gear_intmatrix_op( self.fk2_ctl.attr("worldMatrix"), self.ik_ctl_ref.attr("worldMatrix"), o_node.attr("blend")) mulM_node = applyop.gear_mulmatrix_op( intM_node.attr("output"), self.eff_loc.attr("parentInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulM_node.attr("output")) dm_node.attr("outputScale") >> self.eff_loc.attr("scale") pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 else: mulVal = 1 node.createMulNode(self.roll_att, mulVal, o_node + ".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- pm.pointConstraint(self.mid_ctl_twst_ref, self.tws1_npo, maintainOffset=False) pm.connectAttr(self.mid_ctl.scaleX, self.tws1_loc.scaleX) pm.orientConstraint(self.mid_ctl_twst_ref, self.tws1_npo, maintainOffset=False) o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.tws2_npo.attr("translate")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", self.tws2_npo.attr("rotate")) o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.tws2_rot.attr("parentInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") attribute.setRotOrder(self.tws2_rot, "XYZ") pm.connectAttr(dm_node + ".outputRotate", self.tws2_rot + ".rotate") self.tws0_rot.setAttr("sx", .001) self.tws2_rot.setAttr("sx", .001) add_node = node.createAddNode(self.roundness_att, .001) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) pm.connectAttr(self.armpit_roll_att, self.tws0_rot + ".rotateX") # Roll Shoulder applyop.splineIK(self.getName("rollRef"), self.rollRef, parent=self.root, cParent=self.bone0) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root.attr("sx")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" if self.settings["extraTweak"]: for tweak_ctl in self.tweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll for i, div_cns in enumerate(self.div_cns): if self.settings["supportJoints"]: if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .49 elif i < (self.settings["div0"] + 3): perc = .50 elif i < (self.settings["div0"] + 4): perc = .51 else: perc = .5 + \ (i - self.settings["div0"] - 3.0) * .5 / \ (self.settings["div1"] + 1.0) else: if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .501 else: perc = .5 + \ (i - self.settings["div0"] - 1.0) * .5 / \ (self.settings["div1"] + 1.0) perc = max(.001, min(.990, perc)) # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws2_rot, self.tws1_rot, self.tws0_rot], 1.0 - perc, 40) else: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws0_rot, self.tws1_rot, self.tws2_rot], perc, 40) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) if self.settings["ikTR"]: transform.matchWorldTransform(self.ikRot_ctl, self.match_ikRot) transform.matchWorldTransform(self.fk_ctl[2], self.match_fk2) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref =========================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleLegChainUpvRef"), self.legChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.legChainUpvRef[0], self.ik_ctl, self.upv_cns, mo=True) # Visibilities ------------------------------------- # shape.dispGeometry # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root_ctl, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 else: mulVal = 1 node.createMulNode(self.roll_att, mulVal, o_node + ".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- self.ikhArmRef, self.tmpCrv = applyop.splineIK( self.getName("legRollRef"), self.rollRef, parent=self.root, cParent=self.bone0) pm.pointConstraint(self.mid_ctl, self.tws1_loc, maintainOffset=False) pm.connectAttr(self.mid_ctl.scaleX, self.tws1_loc.scaleX) pm.connectAttr(self.mid_ctl.scaleX, self.tws1B_loc.scaleX) applyop.oriCns(self.mid_ctl, self.tws1_rot, maintainOffset=False) applyop.oriCns(self.mid_ctl, self.tws1B_rot, maintainOffset=False) if self.negate: axis = "xz" axisb = "-xz" else: axis = "-xz" axisb = "xz" applyop.aimCns(self.tws1_loc, self.root_ctl, axis=axis, wupType=4, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.tws1B_loc, self.eff_loc, axis=axisb, wupType=4, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) pm.pointConstraint(self.thick_ctl, self.tws1B_loc, maintainOffset=False) pm.pointConstraint(self.eff_loc, self.tws2_loc, maintainOffset=False) pm.scaleConstraint(self.eff_loc, self.tws2_loc, maintainOffset=False) applyop.oriCns(self.bone1, self.tws2_loc, maintainOffset=False) applyop.oriCns(self.tws_ref, self.tws2_rot) self.tws0_loc.setAttr("sx", .001) self.tws2_loc.setAttr("sx", .001) add_node = node.createAddNode(self.roundness_att, .001) pm.connectAttr(add_node + ".output", self.tws1_rot.attr("sx")) pm.connectAttr(add_node + ".output", self.tws1B_rot.attr("sx")) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root_ctl.attr("sx")) # comp scaling issue dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" if self.settings["extraTweak"]: for tweak_ctl in self.tweak_ctl: for shp in tweak_ctl.getShapes(): pm.connectAttr(self.tweakVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll b_twist = False for i, div_cns in enumerate(self.div_cns): subdiv = False if self.settings["supportJoints"]: if i == len(self.div_cns) - 1 or i == 0: subdiv = 45 else: subdiv = 10 if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): perc = .49 subdiv = 45 elif i < (self.settings["div0"] + 3): perc = .50 subdiv = 45 elif i < (self.settings["div0"] + 4): b_twist = True perc = .51 subdiv = 45 else: perc = (.5 + (i - self.settings["div0"] - 3.0) * .5 / (self.settings["div1"] + 1.0)) else: subdiv = 40 if i < (self.settings["div0"] + 1): perc = i * .5 / (self.settings["div0"] + 1.0) elif i < (self.settings["div0"] + 2): b_twist = True perc = .501 else: perc = .5 + \ (i - self.settings["div0"] - 1.0) * .5 / \ (self.settings["div1"] + 1.0) perc = max(.001, min(.990, perc)) # mid twist distribution if b_twist: mid_twist = self.tws1B_rot else: mid_twist = self.tws1_rot # Roll if self.negate: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws2_rot, mid_twist, self.tws0_rot], 1.0 - perc, subdiv) else: o_node = applyop.gear_rollsplinekine_op( div_cns, [self.tws0_rot, mid_twist, self.tws2_rot], perc, subdiv) pm.connectAttr(self.resample_att, o_node + ".resample") pm.connectAttr(self.absolute_att, o_node + ".absolute") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # NOTE: next line fix the issue on meters. # This is special case becasuse the IK solver from mGear use # the scale as lenght and we have shear # TODO: check for a more clean and elegant solution instead of # re-match the world matrix again # transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0_off) # transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1_off) # transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0) # transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1) # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) # connect_reader pm.parentConstraint(self.bone0, self.readerA, mo=True) pm.parentConstraint(self.bone1, self.readerB, mo=True) # connect auto thickness if self.negate and not self.settings["mirrorMid"]: d_val = -180 / self.length1 else: d_val = 180 / self.length1 div_thick_node = node.createDivNode(self.knee_thickness_att, d_val) node.createMulNode(div_thick_node.outputX, self.readerB.rx, self.thick_lvl.tx) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # 1 bone chain Upv ref ============================== self.ikHandleUpvRef = primitive.addIkHandle( self.root, self.getName("ikHandleLegChainUpvRef"), self.legChainUpvRef, "ikSCsolver") pm.pointConstraint(self.ik_ctl, self.ikHandleUpvRef) pm.parentConstraint(self.legChainUpvRef[0], self.ik_ctl, self.upv_cns, mo=True) # Visibilities ------------------------------------- # shape.dispGeometry # fk fkvis_node = node.createReverseNode(self.blend_att) for shp in self.fk0_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk1_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) for shp in self.fk2_ctl.getShapes(): pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) # ik for shp in self.upv_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ikcns_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.ik_ctl.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) for shp in self.line_ref.getShapes(): pm.connectAttr(self.blend_att, shp.attr("visibility")) # IK Solver ----------------------------------------- out = [self.bone0, self.bone1, self.ctrn_loc, self.eff_loc] o_node = applyop.gear_ikfk2bone_op(out, self.root_ctl, self.ik_ref, self.upv_ctl, self.fk_ctl[0], self.fk_ctl[1], self.fk_ref, self.length0, self.length1, self.negate) pm.connectAttr(self.blend_att, o_node + ".blend") if self.negate: mulVal = -1 else: mulVal = 1 node.createMulNode(self.roll_att, mulVal, o_node + ".roll") # pm.connectAttr(self.roll_att, o_node+".roll") pm.connectAttr(self.scale_att, o_node + ".scaleA") pm.connectAttr(self.scale_att, o_node + ".scaleB") pm.connectAttr(self.maxstretch_att, o_node + ".maxstretch") pm.connectAttr(self.slide_att, o_node + ".slide") pm.connectAttr(self.softness_att, o_node + ".softness") pm.connectAttr(self.reverse_att, o_node + ".reverse") # Twist references --------------------------------- o_node = applyop.gear_mulmatrix_op( self.eff_loc.attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputTranslate", self.tws2_npo.attr("translate")) dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(o_node + ".output", dm_node + ".inputMatrix") pm.connectAttr(dm_node + ".outputRotate", self.tws2_npo.attr("rotate")) # spline IK for twist jnts self.ikhUpLegTwist, self.uplegTwistCrv = applyop.splineIK( self.getName("uplegTwist"), self.uplegTwistChain, parent=self.root, cParent=self.bone0) self.ikhLowLegTwist, self.lowlegTwistCrv = applyop.splineIK( self.getName("lowlegTwist"), self.lowlegTwistChain, parent=self.root, cParent=self.bone1) # references self.ikhUpLegRef, self.tmpCrv = applyop.splineIK( self.getName("uplegRollRef"), self.uplegRollRef, parent=self.root, cParent=self.bone0) self.ikhLowLegRef, self.tmpCrv = applyop.splineIK( self.getName("lowlegRollRef"), self.lowlegRollRef, parent=self.root, cParent=self.eff_loc) self.ikhAuxTwist, self.tmpCrv = applyop.splineIK( self.getName("auxTwist"), self.auxTwistChain, parent=self.root, cParent=self.eff_loc) # setting connexions for ikhUpLegTwist self.ikhUpLegTwist.attr("dTwistControlEnable").set(True) self.ikhUpLegTwist.attr("dWorldUpType").set(4) self.ikhUpLegTwist.attr("dWorldUpAxis").set(3) self.ikhUpLegTwist.attr("dWorldUpVectorZ").set(1.0) self.ikhUpLegTwist.attr("dWorldUpVectorY").set(0.0) self.ikhUpLegTwist.attr("dWorldUpVectorEndZ").set(1.0) self.ikhUpLegTwist.attr("dWorldUpVectorEndY").set(0.0) pm.connectAttr(self.uplegRollRef[0].attr("worldMatrix[0]"), self.ikhUpLegTwist.attr("dWorldUpMatrix")) pm.connectAttr(self.bone0.attr("worldMatrix[0]"), self.ikhUpLegTwist.attr("dWorldUpMatrixEnd")) # setting connexions for ikhAuxTwist self.ikhAuxTwist.attr("dTwistControlEnable").set(True) self.ikhAuxTwist.attr("dWorldUpType").set(4) self.ikhAuxTwist.attr("dWorldUpAxis").set(3) self.ikhAuxTwist.attr("dWorldUpVectorZ").set(1.0) self.ikhAuxTwist.attr("dWorldUpVectorY").set(0.0) self.ikhAuxTwist.attr("dWorldUpVectorEndZ").set(1.0) self.ikhAuxTwist.attr("dWorldUpVectorEndY").set(0.0) pm.connectAttr(self.lowlegRollRef[0].attr("worldMatrix[0]"), self.ikhAuxTwist.attr("dWorldUpMatrix")) pm.connectAttr(self.tws_ref.attr("worldMatrix[0]"), self.ikhAuxTwist.attr("dWorldUpMatrixEnd")) pm.connectAttr(self.auxTwistChain[1].attr("rx"), self.ikhLowLegTwist.attr("twist")) pm.parentConstraint(self.bone1, self.aux_npo, maintainOffset=True) # scale arm length for twist chain (not the squash and stretch) arclen_node = pm.arclen(self.uplegTwistCrv, ch=True) alAttrUpLeg = arclen_node.attr("arcLength") muldiv_nodeArm = pm.createNode("multiplyDivide") pm.connectAttr(arclen_node.attr("arcLength"), muldiv_nodeArm.attr("input1X")) muldiv_nodeArm.attr("input2X").set(alAttrUpLeg.get()) muldiv_nodeArm.attr("operation").set(2) for jnt in self.uplegTwistChain: pm.connectAttr(muldiv_nodeArm.attr("outputX"), jnt.attr("sx")) # scale forearm length for twist chain (not the squash and stretch) arclen_node = pm.arclen(self.lowlegTwistCrv, ch=True) alAttrLowLeg = arclen_node.attr("arcLength") muldiv_nodeLowLeg = pm.createNode("multiplyDivide") pm.connectAttr(arclen_node.attr("arcLength"), muldiv_nodeLowLeg.attr("input1X")) muldiv_nodeLowLeg.attr("input2X").set(alAttrLowLeg.get()) muldiv_nodeLowLeg.attr("operation").set(2) for jnt in self.lowlegTwistChain: pm.connectAttr(muldiv_nodeLowLeg.attr("outputX"), jnt.attr("sx")) # scale compensation for the first twist join dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix[0]"), dm_node.attr("inputMatrix")) pm.connectAttr(dm_node.attr("outputScale"), self.uplegTwistChain[0].attr("inverseScale")) pm.connectAttr(dm_node.attr("outputScale"), self.lowlegTwistChain[0].attr("inverseScale")) # tangent controls muldiv_node = pm.createNode("multiplyDivide") muldiv_node.attr("input2X").set(-1) pm.connectAttr(self.tws1A_npo.attr("rz"), muldiv_node.attr("input1X")) muldiv_nodeBias = pm.createNode("multiplyDivide") pm.connectAttr(muldiv_node.attr("outputX"), muldiv_nodeBias.attr("input1X")) pm.connectAttr(self.roundness_att, muldiv_nodeBias.attr("input2X")) pm.connectAttr(muldiv_nodeBias.attr("outputX"), self.tws1A_loc.attr("rz")) if self.negate: axis = "xz" else: axis = "-xz" applyop.aimCns(self.tws1A_npo, self.tws0_loc, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.lowlegTangentB_loc, self.lowlegTangentA_npo, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) pm.pointConstraint(self.eff_loc, self.lowlegTangentB_loc) muldiv_node = pm.createNode("multiplyDivide") muldiv_node.attr("input2X").set(-1) pm.connectAttr(self.tws1B_npo.attr("rz"), muldiv_node.attr("input1X")) muldiv_nodeBias = pm.createNode("multiplyDivide") pm.connectAttr(muldiv_node.attr("outputX"), muldiv_nodeBias.attr("input1X")) pm.connectAttr(self.roundness_att, muldiv_nodeBias.attr("input2X")) pm.connectAttr(muldiv_nodeBias.attr("outputX"), self.tws1B_loc.attr("rz")) if self.negate: axis = "-xz" else: axis = "xz" applyop.aimCns(self.tws1B_npo, self.tws2_loc, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) applyop.aimCns(self.uplegTangentA_loc, self.uplegTangentB_npo, axis=axis, wupType=2, wupVector=[0, 0, 1], wupObject=self.mid_ctl, maintainOffset=False) # Volume ------------------------------------------- distA_node = node.createDistNode(self.tws0_loc, self.tws1_loc) distB_node = node.createDistNode(self.tws1_loc, self.tws2_loc) add_node = node.createAddNode(distA_node + ".distance", distB_node + ".distance") div_node = node.createDivNode(add_node + ".output", self.root_ctl.attr("sx")) # comp scaling issue dm_node = pm.createNode("decomposeMatrix") pm.connectAttr(self.root.attr("worldMatrix"), dm_node + ".inputMatrix") div_node2 = node.createDivNode(div_node + ".outputX", dm_node + ".outputScaleX") self.volDriver_att = div_node2 + ".outputX" # connecting tangent scaele compensation after volume to # avoid duplicate some nodes distA_node = node.createDistNode(self.tws0_loc, self.mid_ctl) distB_node = node.createDistNode(self.mid_ctl, self.tws2_loc) div_nodeUpLeg = node.createDivNode(distA_node + ".distance", dm_node.attr("outputScaleX")) div_node2 = node.createDivNode(div_nodeUpLeg + ".outputX", distA_node.attr("distance").get()) pm.connectAttr(div_node2.attr("outputX"), self.tws1A_loc.attr("sx")) pm.connectAttr(div_node2.attr("outputX"), self.uplegTangentA_loc.attr("sx")) div_nodeLowLeg = node.createDivNode(distB_node + ".distance", dm_node.attr("outputScaleX")) div_node2 = node.createDivNode(div_nodeLowLeg + ".outputX", distB_node.attr("distance").get()) pm.connectAttr(div_node2.attr("outputX"), self.tws1B_loc.attr("sx")) pm.connectAttr(div_node2.attr("outputX"), self.lowlegTangentB_loc.attr("sx")) # conection curve cnts = [ self.uplegTangentA_loc, self.uplegTangentA_ctl, self.uplegTangentB_ctl, self.kneeTangent_ctl ] applyop.gear_curvecns_op(self.uplegTwistCrv, cnts) cnts = [ self.kneeTangent_ctl, self.lowlegTangentA_ctl, self.lowlegTangentB_ctl, self.lowlegTangentB_loc ] applyop.gear_curvecns_op(self.lowlegTwistCrv, cnts) # Tangent controls vis for shp in self.uplegTangentA_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.uplegTangentB_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.lowlegTangentA_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.lowlegTangentB_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) for shp in self.kneeTangent_ctl.getShapes(): pm.connectAttr(self.tangentVis_att, shp.attr("visibility")) # Divisions ---------------------------------------- # at 0 or 1 the division will follow exactly the rotation of the # controler.. and we wont have this nice tangent + roll for i, div_cns in enumerate(self.div_cns): if i < (self.settings["div0"] + 2): mulmat_node = applyop.gear_mulmatrix_op( self.uplegTwistChain[i] + ".worldMatrix", div_cns + ".parentInverseMatrix") lastUpLegDiv = div_cns else: o_node = self.lowlegTwistChain[i - (self.settings["div0"] + 2)] mulmat_node = applyop.gear_mulmatrix_op( o_node + ".worldMatrix", div_cns + ".parentInverseMatrix") lastLowLegDiv = div_cns dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", div_cns + ".t") pm.connectAttr(dm_node + ".outputRotate", div_cns + ".r") # Squash n Stretch o_node = applyop.gear_squashstretch2_op( div_cns, None, pm.getAttr(self.volDriver_att), "x") pm.connectAttr(self.volume_att, o_node + ".blend") pm.connectAttr(self.volDriver_att, o_node + ".driver") pm.connectAttr(self.st_att[i], o_node + ".stretch") pm.connectAttr(self.sq_att[i], o_node + ".squash") # force translation for last loc arm and foreamr applyop.gear_mulmatrix_op(self.kneeTangent_ctl.worldMatrix, lastUpLegDiv.parentInverseMatrix, lastUpLegDiv, "t") applyop.gear_mulmatrix_op(self.tws2_loc.worldMatrix, lastLowLegDiv.parentInverseMatrix, lastLowLegDiv, "t") # NOTE: next line fix the issue on meters. # This is special case becasuse the IK solver from mGear use the # scale as lenght and we have shear # TODO: check for a more clean and elegant solution instead of # re-match the world matrix again transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0_off) transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1_off) transform.matchWorldTransform(self.fk_ctl[0], self.match_fk0) transform.matchWorldTransform(self.fk_ctl[1], self.match_fk1) # match IK/FK ref pm.parentConstraint(self.bone0, self.match_fk0_off, mo=True) pm.parentConstraint(self.bone1, self.match_fk1_off, mo=True) return
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Auto bend ---------------------------- if self.settings["autoBend"]: mul_node = node.createMulNode( [self.autoBendChain[0].ry, self.autoBendChain[0].rz], [self.sideBend_att, self.frontBend_att]) mul_node.outputX >> self.ik1autoRot_lvl.rz mul_node.outputY >> self.ik1autoRot_lvl.rx self.ikHandleAutoBend = primitive.addIkHandle( self.autoBend_ctl, self.getName("ikHandleAutoBend"), self.autoBendChain, "ikSCsolver") # Tangent position --------------------------------- # common part d = vector.getDistance(self.guide.apos[0], self.guide.apos[-1]) dist_node = node.createDistNode(self.ik0_ctl, self.ik1_ctl) rootWorld_node = node.createDecomposeMatrixNode( self.root.attr("worldMatrix")) div_node = node.createDivNode(dist_node + ".distance", rootWorld_node + ".outputScaleX") div_node = node.createDivNode(div_node + ".outputX", d) # tan0 mul_node = node.createMulNode(self.tan0_att, self.tan0_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan0_npo.attr("ty")) # tan1 mul_node = node.createMulNode(self.tan1_att, self.tan1_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan1_npo.attr("ty")) # Tangent Mid -------------------------------------- if self.settings["centralTangent"]: tanIntMat = applyop.gear_intmatrix_op( self.tan0_npo.attr("worldMatrix"), self.tan1_npo.attr("worldMatrix"), .5) applyop.gear_mulmatrix_op( tanIntMat.attr("output"), self.tan_npo.attr("parentInverseMatrix[0]"), self.tan_npo) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan0_off.attr("translate")) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan1_off.attr("translate")) # Curves ------------------------------------------- op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.maxstretch_att, op + ".maxstretch") pm.connectAttr(self.maxsquash_att, op + ".maxsquash") pm.connectAttr(self.softness_att, op + ".softness") # Volume driver ------------------------------------ crv_node = node.createCurveInfoNode(self.slv_crv) # Division ----------------------------------------- for i in range(self.settings["division"]): # References u = i / (self.settings["division"] - 1.0) if i == 0: # we add extra 10% to the first vertebra u = (1.0 / (self.settings["division"] - 1.0)) / 10 cns = applyop.pathCns(self.div_cns[i], self.slv_crv, False, u, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 0) # front axis is 'X' # Roll intMatrix = applyop.gear_intmatrix_op( self.ik0_ctl + ".worldMatrix", self.ik1_ctl + ".worldMatrix", u) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") # compensate scale reference div_node = node.createDivNode([1, 1, 1], [ rootWorld_node + ".outputScaleX", rootWorld_node + ".outputScaleY", rootWorld_node + ".outputScaleZ" ]) # Squash n Stretch op = applyop.gear_squashstretch2_op(self.scl_transforms[i], self.root, pm.arclen(self.slv_crv), "y", div_node + ".output") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") pm.connectAttr(self.st_att[i], op + ".stretch") pm.connectAttr(self.sq_att[i], op + ".squash") # Controlers if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) else: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.div_cns[i - 1].attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") mul_node = node.createMulNode(div_node + ".output", dm_node + ".outputTranslate") pm.connectAttr(mul_node + ".output", self.fk_npo[i].attr("t")) pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) # Orientation Lock if i == 0: dm_node = node.createDecomposeMatrixNode(self.ik0_ctl + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori0_att) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") elif i == self.settings["division"] - 1: dm_node = node.createDecomposeMatrixNode(self.ik1_ctl + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori1_att) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") # Connections (Hooks) ------------------------------ pm.parentConstraint(self.hip_lvl, self.cnx0) pm.scaleConstraint(self.hip_lvl, self.cnx0) pm.parentConstraint(self.scl_transforms[-1], self.cnx1) pm.scaleConstraint(self.scl_transforms[-1], self.cnx1)
def addOperators(self): """Create operators and set the relations for the component rig Apply operators, constraints, expressions to the hierarchy. In order to keep the code clean and easier to debug, we shouldn't create any new object in this method. """ # Auto bend ---------------------------- if self.settings["autoBend"]: mul_node = node.createMulNode( [self.autoBendChain[0].ry, self.autoBendChain[0].rz], [self.sideBend_att, self.frontBend_att]) mul_node.outputX >> self.ik1autoRot_lvl.rz mul_node.outputY >> self.ik1autoRot_lvl.rx self.ikHandleAutoBend = primitive.addIkHandle( self.autoBend_ctl, self.getName("ikHandleAutoBend"), self.autoBendChain, "ikSCsolver") # Tangent position --------------------------------- # common part d = vector.getDistance(self.guide.apos[1], self.guide.apos[-2]) dist_node = node.createDistNode(self.ik0_ctl, self.ik1_ctl) rootWorld_node = node.createDecomposeMatrixNode( self.root.attr("worldMatrix")) div_node = node.createDivNode(dist_node + ".distance", rootWorld_node + ".outputScaleX") div_node = node.createDivNode(div_node + ".outputX", d) # tan0 mul_node = node.createMulNode(self.tan0_att, self.tan0_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan0_npo.attr("ty")) # tan1 mul_node = node.createMulNode(self.tan1_att, self.tan1_npo.getAttr("ty")) res_node = node.createMulNode(mul_node + ".outputX", div_node + ".outputX") pm.connectAttr(res_node + ".outputX", self.tan1_npo.attr("ty")) # Tangent Mid -------------------------------------- if self.settings["centralTangent"]: tanIntMat = applyop.gear_intmatrix_op( self.tan0_npo.attr("worldMatrix"), self.tan1_npo.attr("worldMatrix"), .5) applyop.gear_mulmatrix_op( tanIntMat.attr("output"), self.tan_npo.attr("parentInverseMatrix[0]"), self.tan_npo) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan0_off.attr("translate")) pm.connectAttr(self.tan_ctl.attr("translate"), self.tan1_off.attr("translate")) # Curves ------------------------------------------- op = applyop.gear_curveslide2_op(self.slv_crv, self.mst_crv, 0, 1.5, .5, .5) pm.connectAttr(self.position_att, op + ".position") pm.connectAttr(self.maxstretch_att, op + ".maxstretch") pm.connectAttr(self.maxsquash_att, op + ".maxsquash") pm.connectAttr(self.softness_att, op + ".softness") # Volume driver ------------------------------------ crv_node = node.createCurveInfoNode(self.slv_crv) # Division ----------------------------------------- tangents = [None, "tan0", "tan1"] for i in range(self.settings["division"]): # References u = i / (self.settings["division"] - 1.0) # check the indx to calculate mid point based on number of division # we want to use the same spine for mannequin and metahuman spine if self.settings["division"] == 4 and i in [1, 2]: u_param = curve.getCurveParamAtPosition( self.slv_crv, self.guide.pos[tangents[i]])[0] cnsType = True elif self.settings["division"] == 3 and i in [1]: u_param = curve.getCurveParamAtPosition( self.slv_crv, self.guide.pos[tangents[i]])[0] cnsType = True else: u_param = u cnsType = False cns = applyop.pathCns(self.div_cns[i], self.slv_crv, cnsType, u_param, True) cns.setAttr("frontAxis", 1) # front axis is 'Y' cns.setAttr("upAxis", 0) # front axis is 'X' # Roll intMatrix = applyop.gear_intmatrix_op( self.ik0_ctl + ".worldMatrix", self.ik1_ctl + ".worldMatrix", u) dm_node = node.createDecomposeMatrixNode(intMatrix + ".output") pm.connectAttr(dm_node + ".outputRotate", self.twister[i].attr("rotate")) pm.parentConstraint(self.twister[i], self.ref_twist[i], maintainOffset=True) pm.connectAttr(self.ref_twist[i] + ".translate", cns + ".worldUpVector") # compensate scale reference div_node = node.createDivNode([1, 1, 1], [ rootWorld_node + ".outputScaleX", rootWorld_node + ".outputScaleY", rootWorld_node + ".outputScaleZ" ]) # Squash n Stretch op = applyop.gear_squashstretch2_op(self.scl_transforms[i], self.root, pm.arclen(self.slv_crv), "y", div_node + ".output") pm.connectAttr(self.volume_att, op + ".blend") pm.connectAttr(crv_node + ".arcLength", op + ".driver") pm.connectAttr(self.st_att[i], op + ".stretch") pm.connectAttr(self.sq_att[i], op + ".squash") # Controlers if i == 0: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.root.attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") pm.connectAttr(dm_node + ".outputTranslate", self.fk_npo[i].attr("t")) else: mulmat_node = applyop.gear_mulmatrix_op( self.div_cns[i].attr("worldMatrix"), self.div_cns[i - 1].attr("worldInverseMatrix")) dm_node = node.createDecomposeMatrixNode(mulmat_node + ".output") mul_node = node.createMulNode(div_node + ".output", dm_node + ".outputTranslate") pm.connectAttr(mul_node + ".output", self.fk_npo[i].attr("t")) pm.connectAttr(dm_node + ".outputRotate", self.fk_npo[i].attr("r")) # Orientation Lock if i == 0: dm_node = node.createDecomposeMatrixNode(self.ik0_ctl + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori0_att) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") elif i == self.settings["division"] - 1: dm_node = node.createDecomposeMatrixNode(self.ik1_ctl + ".worldMatrix") blend_node = node.createBlendNode( [dm_node + ".outputRotate%s" % s for s in "XYZ"], [cns + ".rotate%s" % s for s in "XYZ"], self.lock_ori1_att) self.div_cns[i].attr("rotate").disconnect() pm.connectAttr(blend_node + ".output", self.div_cns[i] + ".rotate") # change parent after operators applied pm.parent(self.scl_transforms[-1], self.fk_ctl[-1]) # Connections (Hooks) ------------------------------ pm.parentConstraint(self.pelvis_lvl, self.cnx0) pm.scaleConstraint(self.pelvis_lvl, self.cnx0) transform.matchWorldTransform(self.scl_transforms[-1], self.cnx1) t = transform.setMatrixPosition(transform.getTransform(self.cnx1), self.guide.apos[-1]) self.cnx1.setMatrix(t, worldSpace=True) pm.parentConstraint(self.scl_transforms[-1], self.cnx1, mo=True) pm.scaleConstraint(self.scl_transforms[-1], self.cnx1)