def update_deformed_shape(source, target, mismatching_topology=True): """ Updates the target shape with the given source shape content :param source: maya shape node :type source: str :param target: maya shape node :type target: str :param mismatching_topology: ignore or not mismatching topologies :type mismatching_topology: bool """ # gets orig shape deform_origin = get_shape_orig(target) # returns as target is not a deformed shape if not deform_origin: return logger.debug("Deformed shape found: {}".format(target)) # returns if source and target shapes don't match if not is_matching_type(source, target): logger.warning( "{} and {} don't have same shape type. passing...".format( source, target)) return # returns if vertices count isn't equal and mismatching isn't requested if not mismatching_topology and not is_matching_count(source, target): logger.warning("{} and {} don't have same shape vertices count." "passing...".format(source, target)) return deform_origin = deform_origin[0] # updates map1 name copy_map1_name(source, deform_origin) # updates on mismatching topology if mismatching_topology and not is_matching_count(source, target): update_deformed_mismatching_shape(source, target, deform_origin) return # update the shape update_shape(source, deform_origin) # update uvs set on target update_uvs_sets(target)
def update_deformed_mismatching_shape(source, target, shape_orig): """ Updates the target shape with the given source shape content :param source: maya shape node :type source: str :param target: maya shape node :type target: str :param shape_orig: shape orig on the target shape :type shape_orig: str """ logger.debug("Running update deformed mismatched shapes") # gets all deformers on the target shape (supported by flex) deformers = get_deformers(target) if len(deformers["skinCluster"]) > 1: logger.warning( "Dual skinning is yet not supported. {} will be used".format( deformers["skinCluster"][0])) # Turns all deformers envelope off set_deformer_state(deformers, False) # creates deformers backups bs_nodes, skin_nodes, cluster_nodes = create_deformers_backups( source, target, shape_orig, deformers) # updates target shape update_shape(source, shape_orig) # updates skinning nodes update_skincluster_node(skin_nodes, deformers["skinCluster"]) # updates blendshapes nodes update_blendshapes_nodes(bs_nodes, deformers["blendShape"]) # update cluster nodes update_clusters_nodes(target, cluster_nodes) # updates uv sets on target shape update_uvs_sets(target) # Turns all deformers envelope ON set_deformer_state(deformers, True) # deletes backups delete_transform_from_nodes(set(bs_nodes).union(skin_nodes))
def analyze_groups(source, target): """ Analyze the shapes found inside the source and target groups :param source: maya transform node :type source: str :param target: maya transform node :type target: str """ logger.debug( "Analysing the following groups - source: {} - target: {}".format( source, target)) # gets the matching shapes matching_shapes = get_matching_shapes_from_group(source, target) # gets mismatching shape types mismatched_types = [ x for x in matching_shapes if not is_matching_type(x, matching_shapes[x]) ] # gets mismatching shape vertices count mismatched_count = [ x for x in matching_shapes if not is_matching_count(x, matching_shapes[x]) ] # gets mismatching shape bounding box mismatched_bbox = [ x for x in matching_shapes if not is_matching_bouding_box(x, matching_shapes[x]) ] logger.info("-" * 90) logger.info("Mismatch shapes types: {}".format(mismatched_types)) logger.info("Mismatch vertices shapes: {}".format(mismatched_count)) logger.info("Mismatch volume shapes: {}".format(mismatched_bbox)) logger.warning("-" * 90) logger.warning("Source missing shapes: {}".format( get_missing_shapes_from_group(source, target))) logger.warning("Target missing shapes: {}".format( get_missing_shapes_from_group(target, source))) logger.warning("-" * 90) return matching_shapes, mismatched_types, mismatched_count, mismatched_bbox
def update_attribute(source, target, attribute_name): """ Updates the given attribute value ..note:: This in a generic method to **setAttr** all type of attributes inside Maya. Using the getSetAttrCmds from the MPLug class allows avoiding to create one method for each type of attribute inside Maya as the setAttr command will differ depending on the attribute type and data. This method is faster than using PyMel attribute set property. :param source: the maya source node :type source: str :param target: the maya target node :type target: str :param attribute_name: the attribute name to set in the given target :type attribute_name: str """ if not cmds.objExists("{}.{}".format(target, attribute_name)): logger.warning( "The current target {} does not have attribute: {}".format( target, attribute_name)) return # checks for locking lock = is_lock_attribute(target, attribute_name) if not lock_unlock_attribute(target, attribute_name, False): logger.warning("The given attribute {} can't be updated on {}".format( attribute_name, target)) return # creates pymel nodes to get and apply attributes from # I am using pymel as they managed to handle default attributes on # referenced nodes correctly. When using MPlug.getSetAttrCmd with kAll # this doesn't return the command correctly when nodes in reference have # the attribute left as default py_source = pm.PyNode(source) py_target = pm.PyNode(target) # sets the attribute value try: attr_value = py_source.attr(attribute_name).get() py_target.attr(attribute_name).set(attr_value) except Exception as e: logger.warning( "The given attribute ({}) can't be updated on {}".format( attribute_name, target)) return e if lock: lock_unlock_attribute(target, attribute_name, True)
def copy_blendshape_node(node, target): """ Copies the given blendshape node into the given target shape :param node: blendshape node :type node: str :param target: target shape node :type target: str :return: copied blenshape node :rtype: str """ logger.debug("Copying blendshape node {} to {}".format(node, target)) # get blendshape targets indices targets_idx = cmds.getAttr("{}.weight".format(node), multiIndices=True) # skip node if no targets where found if not targets_idx: return # list for ignored targets (when they are live connected) ignore = [] # creates blendshape deformer node on target node_copy = cmds.deformer(target, type="blendShape", name="flex_copy_{}" .format(node))[0] # loop on blendshape targets indices for idx in targets_idx: # input target group attribute attr_name = (BLENDSHAPE_TARGET.format(node, idx)) # blendshape target name target_name = cmds.aliasAttr("{}.weight[{}]".format(node, idx), query=True) # checks for empty target if not cmds.getAttr(attr_name, multiIndices=True): continue # loop on actual targets and in-between targets for target in cmds.getAttr(attr_name, multiIndices=True): # target attribute name target_attr = "{}[{}]".format(attr_name, target) # checks for incoming connections on the geometry target if cmds.listConnections("{}.inputGeomTarget".format(target_attr), destination=False): logger.warning("{} can't be updated because it is a live " "target".format(target_name)) ignore.append(idx) continue # updates node copy target destination = target_attr.replace(target_attr.split(".")[0], node_copy) cmds.connectAttr(target_attr, destination, force=True) cmds.disconnectAttr(target_attr, destination) # skips updating target name if this was a life target if idx in ignore: continue # forces the weight attribute to be shown on the blendshape node cmds.setAttr("{}.weight[{}]".format(node_copy, idx), 0) # updates blendshape node attribute name if target_name: cmds.aliasAttr(target_name, "{}.weight[{}]" .format(node_copy, idx)) # gets targets on copied node to see if there is any node with zero target idx = cmds.getAttr("{}.weight".format(node_copy), multiIndices=True) if not idx: cmds.delete(node_copy) return return node_copy