def build(self, no_subdiv=False, num_ctrl=None, degree=3, create_ctrl=True, constraint=False, rot_fol=True, *args, **kwargs): super(Ribbon, self).build(create_grp_anm=create_ctrl, *args, **kwargs) if num_ctrl is not None: self.num_ctrl = num_ctrl nomenclature_rig = self.get_nomenclature_rig() # Create the plane and align it with the selected bones plane_tran = next( (input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None) if plane_tran is None: plane_name = nomenclature_rig.resolve("ribbonPlane") if no_subdiv: # We don't want any subdivision in the plane, so use only 2 bones to create it no_subdiv_degree = 2 if degree < 2: no_subdiv_degree = degree plane_tran = libRigging.create_nurbs_plane_from_joints([self.chain_jnt[0], self.chain_jnt[-1]], degree=no_subdiv_degree, width=self.width) else: plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width) plane_tran.rename(plane_name) plane_tran.setParent(self.grp_rig) self._ribbon_shape = plane_tran.getShape() # Create the follicule needed for the system on the skinned bones self.attach_to_plane(rot_fol) # TODO : Support aim constraint for bones instead of follicle rotation? follicles_grp = pymel.createNode("transform") follicle_grp_name = nomenclature_rig.resolve("follicleGrp") follicles_grp.rename(follicle_grp_name) follicles_grp.setParent(self.grp_rig) for n in self._follicles: n.setParent(follicles_grp) # Create the joints that will drive the ribbon. # TODO: Support other shapes than straight lines... self._ribbon_jnts = libRigging.create_chain_between_objects( self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False) # Group all the joints ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChainGrp') self.ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig) align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False for i, jnt in enumerate(self._ribbon_jnts): # Align the ribbon joints with the real joint to have a better rotation ctrl ribbon_jnt_name = nomenclature_rig.resolve('ribbonJnt{0:02d}'.format(i)) jnt.rename(ribbon_jnt_name) jnt.setParent(self.ribbon_chain_grp) if align_chain: matrix = self.chain_jnt[i].getMatrix(worldSpace=True) jnt.setMatrix(matrix, worldSpace=True) # TODO - Improve skinning smoothing by setting manually the skin... pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True) try: libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0) except ZeroDivisionError, e: pass
def build(self, no_subdiv=False, num_ctrl = None, degree=3, create_ctrl=True, constraint=False, rot_fol=True, *args, **kwargs): super(Ribbon, self).build(create_grp_anm=create_ctrl, *args, **kwargs) if num_ctrl is not None: self.num_ctrl = num_ctrl nomenclature_rig = self.get_nomenclature_rig() # Create the plane and align it with the selected bones plane_tran = next((input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None) if plane_tran is None: plane_name = nomenclature_rig.resolve("ribbonPlane") if no_subdiv: # We don't want any subdivision in the plane, so use only 2 bones to create it no_subdiv_degree = 2 if degree < 2: no_subdiv_degree = degree plane_tran = libRigging.create_nurbs_plane_from_joints([self.chain_jnt[0], self.chain_jnt[-1]], degree=no_subdiv_degree, width=self.width) else: plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width) plane_tran.rename(plane_name) plane_tran.setParent(self.grp_rig) self._ribbon_shape = plane_tran.getShape() # Create the follicule needed for the system on the skinned bones self.attach_to_plane(rot_fol) # TODO : Support aim constraint for bones instead of follicle rotation? follicles_grp = pymel.createNode("transform") follicle_grp_name = nomenclature_rig.resolve("follicleGrp") follicles_grp.rename(follicle_grp_name) follicles_grp.setParent(self.grp_rig) for n in self._follicles: n.setParent(follicles_grp) # Create the joints that will drive the ribbon. # TODO: Support other shapes than straight lines... self._ribbon_jnts = libRigging.create_chain_between_objects( self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False) # Group all the joints ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChainGrp') self.ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig) align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False for i, jnt in enumerate(self._ribbon_jnts): # Align the ribbon joints with the real joint to have a better rotation ctrl ribbon_jnt_name = nomenclature_rig.resolve('ribbonJnt{0:02d}'.format(i)) jnt.rename(ribbon_jnt_name) jnt.setParent(self.ribbon_chain_grp) if align_chain: matrix = self.chain_jnt[i].getMatrix(worldSpace=True) jnt.setMatrix(matrix, worldSpace=True) # TODO - Improve skinning smoothing by setting manually the skin... pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True) try: libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0) except ZeroDivisionError, e: pass
def build(self, rig, num_subdiv = 5, num_ctrl = None, degree=1, create_ctrl=True, constraint=False, rot_fol=False, *args, **kwargs): super(Ribbon, self).build(rig, create_grp_anm=create_ctrl, *args, **kwargs) if num_ctrl is not None: self.num_ctrl = num_ctrl nomenclature_anm = self.get_nomenclature_anm(rig) nomenclature_rig = self.get_nomenclature_rig(rig) #Create the plane and align it with the selected bones plane_tran = next((input for input in self.input if libPymel.isinstance_of_shape(input, pymel.nodetypes.NurbsSurface)), None) if plane_tran is None: plane_name = nomenclature_rig.resolve("ribbonPlane") plane_tran = libRigging.create_nurbs_plane_from_joints(self.chain_jnt, degree=degree, width=self.width) plane_tran.rename(plane_name) plane_tran.setParent(self.grp_rig) self._ribbon_shape = plane_tran.getShape() # TODO: Remove usage of djRivet #Create the follicule needed for the system on the skinned bones for i, jnt in enumerate(self.chain_jnt): pymel.select(jnt, plane_tran) mel.eval("djRivet") #TODO : Support aim constraint for bones instead of follicle rotation? # Apply the skin on the plane and rename follicle from djRivet dj_rivet_grp = pymel.PyNode("djRivetX") follicle_grp_name = nomenclature_rig.resolve("follicle_grp") dj_rivet_grp.rename(follicle_grp_name) dj_rivet_grp.setParent(self.grp_rig) self._follicles = dj_rivet_grp.getChildren() for n in self._follicles: fol_name = nomenclature_rig.resolve("fol") n.rename(fol_name) # Create the joints that will drive the ribbon. # TODO: Support other shapes than straight lines... # TODO: Support ctrl hold/fetch when building/unbuilding. self._ribbon_jnts = libRigging.create_chain_between_objects( self.chain_jnt.start, self.chain_jnt.end, self.num_ctrl, parented=False) # Group all the joints ribbon_chain_grp_name = nomenclature_rig.resolve('ribbonChain' + "_grp") ribbon_chain_grp = pymel.createNode('transform', name=ribbon_chain_grp_name, parent=self.grp_rig) align_chain = True if len(self.chain_jnt) == len(self._ribbon_jnts) else False for i, jnt in enumerate(self._ribbon_jnts): #Align the ribbon joints with the real joint to have a better rotation ctrl if align_chain: matrix = self.chain_jnt[i].getMatrix(worldSpace=True) jnt.setMatrix(matrix, worldSpace=True) jnt.setParent(ribbon_chain_grp) #TODO - Improve skinning smoothing by setting manully the skin... pymel.skinCluster(list(self._ribbon_jnts), plane_tran, dr=1.0, mi=2.0, omi=True) try: libSkinning.assign_weights_from_segments(self._ribbon_shape, self._ribbon_jnts, dropoff=1.0) except ZeroDivisionError, e: pass
def build(self, orient_ik_ctrl=True, num_twist=None, create_bend=None, realign=True, *args, **kwargs): if len(self.chain_jnt) < 2: raise Exception( "Invalid input count. Expected 2, got {0}. {1}".format( len(self.chain_jnt), self.chain_jnt)) # Support some properties that could be redefined at build time if num_twist: self.num_twist = num_twist if create_bend: self.create_bend = create_bend super(Twistbone, self).build(create_grp_anm=self.create_bend, *args, **kwargs) nomenclature_rig = self.get_nomenclature_rig() nomenclature_jnt = self.get_nomenclature_jnt() top_parent = self.chain[0].getParent() jnt_s = self.chain_jnt[0] jnt_e = self.chain_jnt[1] scalable_grp = pymel.createNode('transform') scalable_grp.setParent(self.grp_rig) scalable_grp.rename(nomenclature_rig.resolve('scalable')) # Handle case where the number of twist change. The skin will be lost if self.subjnts and len(self.subjnts) != self.num_twist: self.unassign_twist_weights() pymel.delete(self.subjnts) self.subjnts = [] # Also invalidate ctrls self.ctrls = [] # Generate Subjoints if necessary, they will be use as the skinned one and will be drived by the ribbon and # driver chain if not self.subjnts: self.subjnts = libRigging.create_chain_between_objects( jnt_s, jnt_e, self.num_twist) elif realign: # Position the subjnts at equidistance from each others. num_subjnts = len(self.subjnts) base_tm = jnt_s.getMatrix(worldSpace=True) sp = jnt_s.getTranslation(space='world') ep = jnt_e.getTranslation(space='world') delta = ep - sp for i, subjnt in enumerate(self.subjnts): ratio = float(i) / (num_subjnts - 1) tm = base_tm.copy() tm.translate = delta * ratio + sp subjnt.setMatrix(tm, worldSpace=True) self.subjnts[0].setParent(jnt_s) driver_grp = pymel.createNode('transform') driver_grp.setParent(scalable_grp) driver_grp.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) driver_grp.rename(nomenclature_rig.resolve('driverJnt_grp')) pymel.parentConstraint(jnt_s, driver_grp) # Create a second chain that will drive the rotation of the skinned joint driverjnts = libRigging.create_chain_between_objects( jnt_s, jnt_e, self.num_twist) # Rename the skinning subjnts for i, sub_jnt in enumerate(self.subjnts): sub_jnt.segmentScaleCompensate.set( 0) # Remove segment scale compensate # Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist) jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i)) sub_jnt.rename(jnt_name) driver_refs = [] ctrl_refs = [] # Will be used to drive the ctrl when stretch append for i, driver_jnt in enumerate(driverjnts): driver_jnt.segmentScaleCompensate.set( 0) # Remove segment scale compensate driver_name = nomenclature_jnt.resolve( "twistDriver{0:02d}".format(i)) driver_jnt.rename(driver_name) # Parent all driver joint to the driver group and ensure 0 rotation driver_jnt.setParent(driver_grp) driver_jnt.rotate.set(0, 0, 0) driver_jnt.jointOrient.set(0, 0, 0) # Create a transform that will drive the twist data driver_jnt_ref = pymel.createNode('transform') driver_jnt_ref.setParent(driver_jnt) driver_jnt_ref.setMatrix(driver_jnt.getMatrix(worldSpace=True), worldSpace=True) driver_jnt_ref.rename(driver_name + '_ref') driver_refs.append( driver_jnt_ref ) # Keep them to connect the ref on the subjnts later if self.create_bend: if i != 0 and i != ( len(driverjnts) - 1 ): # There will be no ctrl for the first and last twist jnt ctrl_driver = pymel.createNode("transform") ctrl_driver_name = nomenclature_jnt.resolve( "ctrlDriver{0:02d}".format(i)) ctrl_driver.rename(ctrl_driver_name) ctrl_driver.setParent(driver_grp) ctrl_refs.append(ctrl_driver) if not self.create_bend: # Parent the two extremity of the ribbon to the twist driver associated pymel.pointConstraint(jnt_s, driverjnts[0], mo=False) pymel.pointConstraint(jnt_e, driverjnts[-1], mo=False) mid_idx = math.ceil((self.num_twist / 2.0)) before_mid_idx = math.floor((self.num_twist / 2.0)) if self.create_bend: # Create Ribbon sys_ribbon = Ribbon(self.subjnts, name=nomenclature_rig.resolve("bendRibbon"), rig=self.rig) sys_ribbon.build(create_ctrl=False, degree=3, num_ctrl=self.num_twist, no_subdiv=False, rot_fol=False) self.ctrls = sys_ribbon.create_ctrls(ctrls=self.ctrls, no_extremity=True, constraint_rot=False, refs=self.chain_jnt[1]) # Point constraint the driver jnt on the ribbon jnt to drive the bending for i, driver in enumerate(driverjnts): pymel.pointConstraint(sys_ribbon._ribbon_jnts[i], driver, mo=True) # Aim constraint the driver to create the bend effect. Skip the middle one if it as one # TODO - Find a best way to determine the side aim_vec = [ 1.0, 0.0, 0.0 ] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [ -1.0, 0.0, 0.0 ] aim_vec_inverse = [ -1.0, 0.0, 0.0 ] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [ 1.0, 0.0, 0.0 ] if mid_idx != before_mid_idx and i == (mid_idx - 1): continue if i <= mid_idx - 1: pymel.aimConstraint(sys_ribbon._follicles[i + 1], driver, mo=False, wut=2, wuo=jnt_s, aim=aim_vec, u=[0.0, 1.0, 0.0]) else: pymel.aimConstraint(sys_ribbon._follicles[i - 1], driver, mo=False, wut=2, wuo=jnt_s, aim=aim_vec_inverse, u=[0.0, 1.0, 0.0]) for ctrl, ref in zip(self.ctrls, ctrl_refs): #libAttr.lock_hide_rotation(ctrl) libAttr.lock_hide_scale(ctrl) ctrl.setParent(self.grp_anm) pymel.parentConstraint(ref, ctrl.offset, mo=True) # We don't want the ribbon to scale with the system since it will follow with it's bone sys_ribbon.grp_rig.setParent(self.grp_rig) # Ensure that the ribbon jnts are following the start jnt correctly pymel.parentConstraint(jnt_s, sys_ribbon.ribbon_chain_grp, mo=True) # Parent the two extremity of the ribbon to the twist driver associated pymel.pointConstraint(jnt_s, sys_ribbon._ribbon_jnts[0], mo=False) pymel.pointConstraint(jnt_e, sys_ribbon._ribbon_jnts[-1], mo=False) # Create the first non roll system nonroll_sys_start = NonRollJoint() nonroll_sys_start.build(jnt_s, name=nomenclature_rig.resolve("nonrollStart")) nonroll_sys_start.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) if top_parent: pymel.orientConstraint(top_parent, nonroll_sys_start.node, maintainOffset=True) pymel.pointConstraint(jnt_s, nonroll_sys_start.node) pymel.parentConstraint(jnt_s, nonroll_sys_start.ikHandle, maintainOffset=True) # Create the second non-roll system nonroll_sys_end = NonRollJoint() nonroll_sys_end.build(jnt_e, name=nomenclature_rig.resolve("nonrollEnd")) nonroll_sys_end.setMatrix(jnt_e.getMatrix(worldSpace=True), worldSpace=True) # nonroll_sys_end.setTranslation(jnt_e.getTranslation(space='world'), space='world') pymel.orientConstraint(jnt_s, nonroll_sys_end.node, maintainOffset=True) pymel.pointConstraint(jnt_e, nonroll_sys_end.node) pymel.parentConstraint(jnt_e, nonroll_sys_end.ikHandle, maintainOffset=True) # Setup twist extraction using the two non-roll twist extractor # The start ref will be connected as inverted to prevent it to twist mdl_reverse_rotx_start = pymel.createNode("multDoubleLinear") pymel.connectAttr(nonroll_sys_start.twist_extractor.rotateX, mdl_reverse_rotx_start.input1) mdl_reverse_rotx_start.input2.set(-1.0) pymel.connectAttr(mdl_reverse_rotx_start.output, driver_refs[0].rotateX) # The last ref will be directly be connected pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, driver_refs[-1].rotateX) # Finally, compute the twist value for the middle twists joints, also connect the ctrl rotation in the system # for more control twist_split_len = len(driver_refs[1:-1]) for i, ref in enumerate(driver_refs[1:-1]): blend_w_node = pymel.createNode("blendWeighted") pymel.connectAttr(mdl_reverse_rotx_start.output, blend_w_node.input[0]) pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, blend_w_node.input[1]) # Compute the weight of each twist in the system weight_start = (1.0 / (twist_split_len + 1)) * (twist_split_len - i) weight_end = 1.0 - weight_start blend_w_node.weight[0].set(weight_start) blend_w_node.weight[1].set(weight_end) blend_w_node.weight[2].set(1.0) pymel.connectAttr(blend_w_node.output, ref.rotateX) if self.create_bend: pymel.connectAttr(self.ctrls[i].rotateX, blend_w_node.input[2]) pymel.connectAttr(self.ctrls[i].rotateY, ref.rotateY) pymel.connectAttr(self.ctrls[i].rotateZ, ref.rotateZ) # Cleanup nonroll_sys_start.setParent(scalable_grp) nonroll_sys_end.setParent(scalable_grp) # Compute the Stretch attr_stretch_raw = libRigging.create_stretch_node_between_2_bones( jnt_s, jnt_e, self.grp_rig.globalScale) pymel.connectAttr(attr_stretch_raw, driver_grp.scaleX) # Connect global scale pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleX) pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleY) pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleZ) for driver_ref, jnt in zip(driver_refs, self.subjnts): # If we have the bend, just orient constraint cause the subjnt are constrained to the follicle of the ribbon if self.create_bend: pymel.orientConstraint(driver_ref, jnt, mo=False) else: pymel.parentConstraint(driver_ref, jnt, mo=False) ''' pymel.connectAttr(driver_ref.scaleX, jnt.scaleX) pymel.connectAttr(driver_ref.scaleY, jnt.scaleY) pymel.connectAttr(driver_ref.scaleZ, jnt.scaleZ) ''' if self.auto_skin: self.assign_twist_weights()
def build(self, orient_ik_ctrl=True, num_twist=None, create_bend=None, realign=True, *args, **kwargs): if len(self.chain_jnt) < 2: raise Exception("Invalid input count. Expected 2, got {0}. {1}".format(len(self.chain_jnt), self.chain_jnt)) # Support some properties that could be redefined at build time if num_twist: self.num_twist = num_twist if create_bend: self.create_bend = create_bend super(Twistbone, self).build(create_grp_anm=self.create_bend, *args, **kwargs) nomenclature_rig = self.get_nomenclature_rig() nomenclature_jnt = self.get_nomenclature_jnt() top_parent = self.chain[0].getParent() jnt_s = self.chain_jnt[0] jnt_e = self.chain_jnt[1] scalable_grp = pymel.createNode('transform') scalable_grp.setParent(self.grp_rig) scalable_grp.rename(nomenclature_rig.resolve('scalable')) # Handle case where the number of twist change. The skin will be lost if self.subjnts and len(self.subjnts) != self.num_twist: self.unassign_twist_weights() pymel.delete(self.subjnts) self.subjnts = [] # Also invalidate ctrls self.ctrls = [] # Generate Subjoints if necessary, they will be use as the skinned one and will be drived by the ribbon and # driver chain if not self.subjnts: self.subjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, self.num_twist) elif realign: # Position the subjnts at equidistance from each others. num_subjnts = len(self.subjnts) base_tm = jnt_s.getMatrix(worldSpace=True) sp = jnt_s.getTranslation(space='world') ep = jnt_e.getTranslation(space='world') delta = ep - sp for i, subjnt in enumerate(self.subjnts): ratio = float(i) / (num_subjnts-1) tm = base_tm.copy() tm.translate = delta * ratio + sp subjnt.setMatrix(tm, worldSpace=True) self.subjnts[0].setParent(jnt_s) driver_grp = pymel.createNode('transform') driver_grp.setParent(scalable_grp) driver_grp.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) driver_grp.rename(nomenclature_rig.resolve('driverJnt_grp')) pymel.parentConstraint(jnt_s, driver_grp) # Create a second chain that will drive the rotation of the skinned joint driverjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, self.num_twist) # Rename the skinning subjnts for i, sub_jnt in enumerate(self.subjnts): sub_jnt.segmentScaleCompensate.set(0) # Remove segment scale compensate # Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist) jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i)) sub_jnt.rename(jnt_name) driver_refs = [] ctrl_refs = [] # Will be used to drive the ctrl when stretch append for i, driver_jnt in enumerate(driverjnts): driver_jnt.segmentScaleCompensate.set(0) # Remove segment scale compensate driver_name = nomenclature_jnt.resolve("twistDriver{0:02d}".format(i)) driver_jnt.rename(driver_name) # Parent all driver joint to the driver group and ensure 0 rotation driver_jnt.setParent(driver_grp) driver_jnt.rotate.set(0, 0, 0) driver_jnt.jointOrient.set(0, 0, 0) # Create a transform that will drive the twist data driver_jnt_ref = pymel.createNode('transform') driver_jnt_ref.setParent(driver_jnt) driver_jnt_ref.setMatrix(driver_jnt.getMatrix(worldSpace=True), worldSpace=True) driver_jnt_ref.rename(driver_name + '_ref') driver_refs.append(driver_jnt_ref) # Keep them to connect the ref on the subjnts later if self.create_bend: if i != 0 and i != (len(driverjnts) - 1): # There will be no ctrl for the first and last twist jnt ctrl_driver = pymel.createNode("transform") ctrl_driver_name = nomenclature_jnt.resolve("ctrlDriver{0:02d}".format(i)) ctrl_driver.rename(ctrl_driver_name) ctrl_driver.setParent(driver_grp) ctrl_refs.append(ctrl_driver) if not self.create_bend: # Parent the two extremity of the ribbon to the twist driver associated pymel.pointConstraint(jnt_s, driverjnts[0], mo=False) pymel.pointConstraint(jnt_e, driverjnts[-1], mo=False) mid_idx = math.ceil((self.num_twist/2.0)) before_mid_idx = math.floor((self.num_twist/2.0)) if self.create_bend: # Create Ribbon sys_ribbon = Ribbon(self.subjnts, name=nomenclature_rig.resolve("bendRibbon"), rig=self.rig) sys_ribbon.build(create_ctrl=False, degree=3, num_ctrl=self.num_twist, no_subdiv=False, rot_fol=False) self.ctrls = sys_ribbon.create_ctrls(ctrls=self.ctrls, no_extremity=True, constraint_rot=False, refs=self.chain_jnt[1]) # Point constraint the driver jnt on the ribbon jnt to drive the bending for i, driver in enumerate(driverjnts): pymel.pointConstraint(sys_ribbon._ribbon_jnts[i], driver, mo=True) # Aim constraint the driver to create the bend effect. Skip the middle one if it as one # TODO - Find a best way to determine the side aim_vec = [1.0, 0.0, 0.0] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [-1.0, 0.0, 0.0] aim_vec_inverse = [-1.0, 0.0, 0.0] if nomenclature_rig.side == nomenclature_rig.SIDE_L else [1.0, 0.0, 0.0] if mid_idx != before_mid_idx and i == (mid_idx - 1): continue if i <= mid_idx - 1: pymel.aimConstraint(sys_ribbon._follicles[i + 1], driver, mo=False, wut=2, wuo=jnt_s, aim=aim_vec, u=[0.0, 1.0, 0.0]) else: pymel.aimConstraint(sys_ribbon._follicles[i - 1], driver, mo=False, wut=2, wuo=jnt_s, aim=aim_vec_inverse, u=[0.0, 1.0, 0.0]) for ctrl, ref in zip(self.ctrls, ctrl_refs): #libAttr.lock_hide_rotation(ctrl) libAttr.lock_hide_scale(ctrl) ctrl.setParent(self.grp_anm) pymel.parentConstraint(ref, ctrl.offset, mo=True) # We don't want the ribbon to scale with the system since it will follow with it's bone sys_ribbon.grp_rig.setParent(self.grp_rig) # Ensure that the ribbon jnts are following the start jnt correctly pymel.parentConstraint(jnt_s, sys_ribbon.ribbon_chain_grp, mo=True) # Parent the two extremity of the ribbon to the twist driver associated pymel.pointConstraint(jnt_s, sys_ribbon._ribbon_jnts[0], mo=False) pymel.pointConstraint(jnt_e, sys_ribbon._ribbon_jnts[-1], mo=False) # Create the first non roll system nonroll_sys_start = NonRollJoint() nonroll_sys_start.build(jnt_s, name=nomenclature_rig.resolve("nonrollStart")) nonroll_sys_start.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) if top_parent: pymel.orientConstraint(top_parent, nonroll_sys_start.node, maintainOffset=True) pymel.pointConstraint(jnt_s, nonroll_sys_start.node) pymel.parentConstraint(jnt_s, nonroll_sys_start.ikHandle, maintainOffset=True) # Create the second non-roll system nonroll_sys_end = NonRollJoint() nonroll_sys_end.build(jnt_e, name=nomenclature_rig.resolve("nonrollEnd")) nonroll_sys_end.setMatrix(jnt_e.getMatrix(worldSpace=True), worldSpace=True) # nonroll_sys_end.setTranslation(jnt_e.getTranslation(space='world'), space='world') pymel.orientConstraint(jnt_s, nonroll_sys_end.node, maintainOffset=True) pymel.pointConstraint(jnt_e, nonroll_sys_end.node) pymel.parentConstraint(jnt_e, nonroll_sys_end.ikHandle, maintainOffset=True) # Setup twist extraction using the two non-roll twist extractor # The start ref will be connected as inverted to prevent it to twist mdl_reverse_rotx_start = pymel.createNode("multDoubleLinear") pymel.connectAttr(nonroll_sys_start.twist_extractor.rotateX, mdl_reverse_rotx_start.input1) mdl_reverse_rotx_start.input2.set(-1.0) pymel.connectAttr(mdl_reverse_rotx_start.output, driver_refs[0].rotateX) # The last ref will be directly be connected pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, driver_refs[-1].rotateX) # Finally, compute the twist value for the middle twists joints, also connect the ctrl rotation in the system # for more control twist_split_len = len(driver_refs[1:-1]) for i, ref in enumerate(driver_refs[1:-1]): blend_w_node = pymel.createNode("blendWeighted") pymel.connectAttr(mdl_reverse_rotx_start.output, blend_w_node.input[0]) pymel.connectAttr(nonroll_sys_end.twist_extractor.rotateX, blend_w_node.input[1]) # Compute the weight of each twist in the system weight_start = (1.0 / (twist_split_len + 1)) * (twist_split_len - i) weight_end = 1.0 - weight_start blend_w_node.weight[0].set(weight_start) blend_w_node.weight[1].set(weight_end) blend_w_node.weight[2].set(1.0) pymel.connectAttr(blend_w_node.output, ref.rotateX) if self.create_bend: pymel.connectAttr(self.ctrls[i].rotateX, blend_w_node.input[2]) pymel.connectAttr(self.ctrls[i].rotateY, ref.rotateY) pymel.connectAttr(self.ctrls[i].rotateZ, ref.rotateZ) # Cleanup nonroll_sys_start.setParent(scalable_grp) nonroll_sys_end.setParent(scalable_grp) # Compute the Stretch attr_stretch_raw = libRigging.create_stretch_node_between_2_bones(jnt_s, jnt_e, self.grp_rig.globalScale) pymel.connectAttr(attr_stretch_raw, driver_grp.scaleX) # Connect global scale pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleX) pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleY) pymel.connectAttr(self.grp_rig.globalScale, scalable_grp.scaleZ) for driver_ref, jnt in zip(driver_refs, self.subjnts): # If we have the bend, just orient constraint cause the subjnt are constrained to the follicle of the ribbon if self.create_bend: pymel.orientConstraint(driver_ref, jnt, mo=False) else: pymel.parentConstraint(driver_ref, jnt, mo=False) ''' pymel.connectAttr(driver_ref.scaleX, jnt.scaleX) pymel.connectAttr(driver_ref.scaleY, jnt.scaleY) pymel.connectAttr(driver_ref.scaleZ, jnt.scaleZ) ''' if self.auto_skin: self.assign_twist_weights()
def build(self, rig, orient_ik_ctrl=True, create_boxes=True, *args, **kwargs): if len(self.chain_jnt) < 2: raise Exception("Invalid input count. Expected 2, got {0}. {1}".format(len(self.chain_jnt), self.chain_jnt)) super(Twistbone, self).build(rig, create_grp_anm=False, *args, **kwargs) nomenclature_rig = self.get_nomenclature_rig(rig) nomenclature_jnt = self.get_nomenclature_jnt(rig) jnt_s = self.chain_jnt[0] jnt_e = self.chain_jnt[1] # Create curve from input joints (we'll use maya splineIKEffector for our upnodes. num_steps = 2 self.ikCurve = libRigging.create_nurbsCurve_from_joints(jnt_s, jnt_e, 2 if num_steps > 2 else 1) self.ikCurve.rename(self.name + "_crv") pymel.parentConstraint(jnt_s, self.ikCurve, maintainOffset=True) # Generate Subjoints if necessary if not self.subjnts: self.subjnts = libRigging.create_chain_between_objects(jnt_s, jnt_e, 4) #TODO : Use the nomeclature system to name the bones for i, sub_jnt in enumerate(self.subjnts): sub_jnt.segmentScaleCompensate.set(0) #Remove segment scale compensate #Right now, we take into consideration that the system will be named Side_SysName(Ex:Upperarm_Twist) jnt_name = nomenclature_jnt.resolve("twist{0:02d}".format(i)) sub_jnt.rename(jnt_name) # Create splineIK #Do not connect the stretch to prevent scaling problem #TODO : If a stretch system exist on the input, we need to find a way to connect it to the twist system splineIK = SplineIK(self.subjnts +[self.ikCurve], name=nomenclature_rig.resolve("splineik")) splineIK.bStretch = False splineIK.build(rig, create_grp_anm=False, stretch=False) self.ikCurve.setParent(splineIK.grp_rig) nonroll_1 = NonRollJoint() nonroll_1.build() nonroll_1.rename(nomenclature_rig.resolve('nonroll_s')) nonroll_1.start.rename(nomenclature_jnt.resolve("nonroll_s_start")) nonroll_1.end.rename(nomenclature_jnt.resolve("nonroll_s_end")) jnt_s_parent = jnt_s.getParent() nonroll_1.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) if jnt_s_parent: pymel.parentConstraint(jnt_s_parent, nonroll_1.node, maintainOffset=True, skipTranslate=['x', 'y', 'z']) pymel.pointConstraint(jnt_s, nonroll_1.node) pymel.parentConstraint(jnt_s, nonroll_1.ikHandle, maintainOffset=True) nonroll_2 = NonRollJoint() nonroll_2.build() nonroll_2.rename(nomenclature_rig.resolve('nonroll_e')) nonroll_2.start.rename(nomenclature_jnt.resolve("nonroll_e_start")) nonroll_2.end.rename(nomenclature_jnt.resolve("nonroll_e_end")) nonroll_2.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) nonroll_2.setTranslation(jnt_e.getTranslation(space='world'), space='world') pymel.parentConstraint(jnt_s, nonroll_2.node, maintainOffset=True, skipTranslate=['x', 'y', 'z']) pymel.pointConstraint(jnt_e, nonroll_2.node) pymel.parentConstraint(jnt_e, nonroll_2.ikHandle, maintainOffset=True) twist_info = pymel.createNode('transform') twist_info.rename('twist_info') twist_info.setMatrix(nonroll_2.start.getMatrix(worldSpace=True), worldSpace=True) twist_info.setParent(nonroll_2.start) pymel.aimConstraint(nonroll_2.end, twist_info, worldUpType=2, worldUpObject=jnt_e) ref_end = pymel.createNode('transform') ref_end.rename('ref_end') ref_end.setMatrix(nonroll_2.getMatrix(worldSpace=True), worldSpace=True) ref_end.setParent(nonroll_2.node) pymel.connectAttr(twist_info.rotate, ref_end.rotate) # Create the upnodes upnode_s = pymel.createNode('transform', name='upnode_s') upnode_s.setMatrix(jnt_s.getMatrix(worldSpace=True)) upnode_e = pymel.createNode('transform', name='upnode_e') upnode_e.setMatrix(jnt_s.getMatrix(worldSpace=True), worldSpace=True) upnode_e.setTranslation(jnt_e.getTranslation(space='world'), space='world') pymel.parentConstraint(nonroll_1.start, upnode_s) pymel.parentConstraint(ref_end, upnode_e) # Cleanup nonroll_1.setParent(self.grp_rig) nonroll_2.setParent(self.grp_rig) upnode_s.setParent(self.grp_rig) upnode_e.setParent(self.grp_rig) splineIK.grp_rig.setParent(self.grp_rig) # Configure splineIK upnodes parameters splineIK.ikHandle.dTwistControlEnable.set(1) splineIK.ikHandle.dWorldUpType.set(4) # Object Rotation Up (Start End) #TODO : Find a better way to define the foward axis than the side string if nomenclature_rig.side == nomenclature_rig.SIDE_R: splineIK.ikHandle.dForwardAxis.set(1) #splineIK.ikHandle.dWorldUpAxis.set(1) pymel.connectAttr(upnode_s.xformMatrix, splineIK.ikHandle.dWorldUpMatrix) pymel.connectAttr(upnode_e.xformMatrix, splineIK.ikHandle.dWorldUpMatrixEnd) #Compute the Stretch attr_stretch_raw = libRigging.create_stretch_node_between_2_bones(jnt_s, jnt_e, self.grp_rig.globalScale) #for subjnt in self.subjnts[1:]: pymel.connectAttr(attr_stretch_raw, self.subjnts[0].scaleX) #Connect global scale pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleX) pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleY) pymel.connectAttr(self.grp_rig.globalScale, self.grp_rig.scaleZ) # Unparent the twistbones so they squash correctly, even in a Game-Engine scenario. if self.subjnts[0].getParent() != self.chain_jnt.start: self.subjnts[0].setParent(self.chain_jnt.start) if self.auto_skin: skin_deformers = self.get_skinClusters_from_inputs() for skin_deformer in skin_deformers: # Ensure the source joint is in the skinCluster influences influenceObjects = skin_deformer.influenceObjects() if self.chain_jnt.start not in influenceObjects: continue # Add new joints as influence. for subjnt in self.subjnts: if subjnt in influenceObjects: continue skin_deformer.addInfluence(subjnt, lockWeights=True, weight=0.0) subjnt.lockInfluenceWeights.set(False) # TODO : Automatically skin the twistbones for mesh in self.get_farest_affected_meshes(rig): print("{1} --> Assign skin weights on {0}.".format(mesh.name(), self.name)) libSkinning.transfer_weights_from_segments(mesh, self.chain_jnt.start, self.subjnts) '''