Beispiel #1
0
def update_user_attributes(source, target):
    """ Updates the target shape attributes with the given source shape content

    :param source: maya shape node
    :type source: str

    :param target: maya shape node
    :type target: str

    .. note:: This method loops twice on the use attributes. One time to add
              the missing attributes and the second to set their value. This
              allows avoiding issues when dealing with child attributes.
    """

    # get user defined attributes
    user_attributes = cmds.listAttr(source, userDefined=True)

    if not user_attributes:
        return

    logger.debug("Updating user attributes on {}".format(target))

    # loop on user defined attributes if any to ---> addAttr
    for attr in user_attributes:
        # adds attribute on shape
        add_attribute(source, target, attr)

    # loop on user defined attributes if any to ---> setAttr
    for attr in user_attributes:
        # updates the attribute values
        update_attribute(source, target, attr)
def update_shape(source, target):
    """ Connect the shape output from source to the input shape on target

    :param source: maya shape node
    :type source: str

    :param target: maya shape node
    :type target: str
    """

    # clean uvs on mesh nodes
    clean_uvs_sets(target)

    # get attributes names
    attributes = get_shape_type_attributes(source)

    logger.debug("Updating shape: {} using --> {}".format(target, source))

    # updates the shape
    cmds.connectAttr("{}.{}".format(source, attributes["output"]),
                     "{}.{}".format(target, attributes["input"]),
                     force=True)

    # forces shape evaluation to achieve the update
    cmds.dgeval("{}.{}".format(target, attributes["output"]))

    # finish shape update
    cmds.disconnectAttr("{}.{}".format(source, attributes["output"]),
                        "{}.{}".format(target, attributes["input"]))
    def wrapper_function(*args, **kwars):

        if not cmds.about(batch=True):
            # get panels views
            model_panels = cmds.getPanel(type="modelPanel")

            logger.debug("Isolating views")

            # isolate views
            for model_panel in model_panels:
                cmds.isolateSelect(model_panel, state=True)

                # clear isolated panel set
                isolate_set = cmds.isolateSelect(model_panel, query=True,
                                                 viewObjects=True)
                if isolate_set:
                    cmds.sets(clear=isolate_set)

            # forces view refresh
            cmds.refresh()

        # runs decorated function
        function_exec = function(*args, **kwars)

        return function_exec
Beispiel #4
0
def update_transformed_shape(source, target, hold_transform):
    """ 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 hold_transform: keeps the transform node position values
    :type hold_transform: bool
    """

    deform_origin = get_shape_orig(target)

    if deform_origin:
        return

    logger.debug("Transformed shape found: {}".format(target))

    # maintain transform on target
    if hold_transform:
        update_shape(source, target)

    # update target transform
    else:
        update_transform(source, target)
    def wrapper_function(*args, **kwars):
        # runs decorated function
        function_exec = function(*args, **kwars)

        if not cmds.about(batch=True):

            logger.debug("Focusing Flex window: {}".format(FLEX_UI_NAME))
            # sets focus on Flex window
            cmds.setFocus(FLEX_UI_NAME)

        return function_exec
    def wrapper_function(*args, **kwars):
        # runs decorated function
        function_exec = function(*args, **kwars)

        # get panels and set isolation state to off
        if not cmds.about(batch=True):
            logger.debug("Showing views")

            model_panels = cmds.getPanel(type="modelPanel")
            for model_panel in model_panels:
                cmds.isolateSelect(model_panel, state=False)

        return function_exec
    def wrapper_function(*args, **kwars):
        # gets current time
        t1 = time.time()

        # runs decorated function
        function_exec = function(*args, **kwars)

        # gets new time
        t2 = time.time() - t1

        logger.debug('function: {} took {}'.format(function.__name__, t2))

        return function_exec
    def wrapper_function(*args, **kwars):

        # gets selection
        sel = cmds.ls(selection=True)
        logger.debug("Holding selection: {}".format(sel))

        # runs decorated function
        function_exec = function(*args, **kwars)

        # selects back
        cmds.select(sel, replace=True)

        return function_exec
Beispiel #9
0
def create_wrap(source, target, intermediate=None):
    """ Creates a wrap deformer on the target by using source as driver

    :param source: the maya source node
    :type source: str

    :param target: the maya target node
    :type target: str

    :param intermediate: the intermediate shape to use on the warp node
    :type intermediate: str

    :return: wrap node
    :rtype: str
    """

    logger.debug("Creating wrap deformer for {} using {}".format(
        target, source))

    # creates the deformer
    target_type = cmds.objectType(target)
    wrap_node = cmds.deformer(target, type="wrap", name="flex_warp")[0]
    cmds.setAttr("{}.exclusiveBind".format(wrap_node), 1)
    cmds.setAttr("{}.autoWeightThreshold".format(wrap_node), 1)
    cmds.setAttr("{}.dropoff[0]".format(wrap_node), 4.0)

    # sets settings for nurbs type shapes
    if target_type == "nurbsSurface" or target_type == "nurbsCurve":
        cmds.setAttr("{}.nurbsSamples[0]".format(wrap_node), 10)
    # sets settings for mesh type shapes
    else:
        cmds.setAttr("{}.inflType[0]".format(wrap_node), 2)

    # gets attributes types for the given target
    attrs = get_shape_type_attributes(target)

    # filters intermediate shape
    intermediate_shape = filter_shape_orig(source, intermediate)

    # connects the wrap node to the source
    cmds.connectAttr("{}.{}".format(intermediate_shape, attrs["output_world"]),
                     "{}.basePoints[0]".format(wrap_node),
                     force=True)
    cmds.connectAttr("{}.{}".format(source, attrs["output"]),
                     "{}.driverPoints[0]".format(wrap_node),
                     force=True)
    cmds.connectAttr("{}.worldMatrix[0]".format(target),
                     "{}.geomMatrix".format(wrap_node),
                     force=True)

    return wrap_node
Beispiel #10
0
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)
Beispiel #11
0
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))
Beispiel #12
0
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
Beispiel #13
0
def update_plugin_attributes(source, target):
    """ Updates all maya plugin defined attributes

    :param source: maya shape node
    :type source: str

    :param target: maya shape node
    :type target: str
    """

    source_attrs = cmds.listAttr(source, fromPlugin=True) or []
    taget_attrs = cmds.listAttr(target, fromPlugin=True) or []

    logger.debug("Updating  plugin attributes on {}".format(target))
    for attribute in source_attrs:
        if attribute in taget_attrs:
            update_attribute(source, target, attribute)
def create_duplicate(shape, duplicate_name):
    """ Creates a shape node duplicate

    :param shape: the shape node to duplicate
    :type shape: str

    :param name: the name for the duplicate
    :type name: str

    :return: the duplicated shape node
    :rtype: str
    """

    logger.debug("Creating shape duplicate for {}".format(shape))
    shape_holder = cmds.createNode(cmds.objectType(shape),
                                   name="{}Shape".format(duplicate_name))
    cmds.rename(shape_holder, "{}".format(shape_holder))
    update_shape(shape, shape_holder)

    return shape_holder
def set_deformer_state(deformers, enable):
    """ Set envelope attribute to one on the given deformers dictionary

    :param deformers: dict containing the deformers set by type
    :type deformers: type

    :param enable: on or off state for the given deformers
    :type enable: bool
    """

    logger.debug("Setting deformers {} envelop enable to: {}"
                 .format(deformers, enable))

    # Loop of the deformer dict and set state
    for deformer_type in deformers:
        for i in deformers[deformer_type] or []:
            if enable:
                set_deformer_on(i)
                continue
            set_deformer_off(i)
def copy_map1_name(source, target):
    """ Copies the name of the uvSet at index zero (map1) to match it

    :param source: maya shape node
    :type source: str

    :param target: maya shape node
    :type target: str
    """

    if not is_matching_type(source, target):
        return

    source_uv_name = cmds.getAttr("{}.uvSet[0].uvSetName".format(source))

    try:
        cmds.setAttr("{}.uvSet[0].uvSetName".format(target), source_uv_name,
                     type="string")
    except RuntimeError:
        logger.debug("{} doesn't not have uvs, skipping udpate map1 name"
                     .format(target))
        return
Beispiel #17
0
    def __init__(self, parent=None):
        """ Creates all the user interface widgets

        :param parent: the parent widget for the Flex dialog widget
        :type parent: PySide2.QtWidgets
        """
        super(FlexAnalyzeDialog, self).__init__(parent=parent)

        logger.debug("Analyze widget initialised")

        # sets window rules
        self.setObjectName(FLEX_ANALYZE_NAME)
        self.setWindowTitle("mGear: Flex analyze shapes")
        self.setMinimumWidth(500)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        # creates the icons
        self.green_icon = QtGui.QIcon()
        image = QtGui.QPixmap("{}/green.png".format(get_resources_path()))
        self.green_icon.addPixmap(image)

        self.red_icon = QtGui.QIcon()
        image = QtGui.QPixmap("{}/red.png".format(get_resources_path()))
        self.red_icon.addPixmap(image)

        self.yellow_icon = QtGui.QIcon()
        image = QtGui.QPixmap("{}/yellow.png".format(get_resources_path()))
        self.yellow_icon.addPixmap(image)

        # creates layout
        layout = QtWidgets.QVBoxLayout()
        layout.setMargin(0)

        # setup table
        self.setLayout(layout)
        self.__create_table()

        # add widgets
        layout.addWidget(self.table_widget)
Beispiel #18
0
    def __init__(self, parent=None):
        """ Creates all the user interface widgets

        :param parent: the parent widget for the Flex dialog widget
        :type parent: PySide2.QtWidgets
        """
        super(FlexDialog, self).__init__(parent=parent)

        logger.debug("Flex widget initialised")

        # sets window rules
        self.setObjectName(FLEX_UI_NAME)
        self.setWindowTitle("mGear: Flex (rig updater)")
        self.setStyleSheet(self.__style_sheet())
        self.setMinimumWidth(350)
        self.setMinimumHeight(100)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        # creates widgets
        self.layout_widgets()
        self.models_groups_widgets()
        self.options_widgets()
        self.run_widgets()
Beispiel #19
0
def update_transform(source, target):
    """ Updates the transform node on target

    This method creates a duplicate of the transform node on source and
    uses is as the new parent transform for the target shape

    :param source: maya shape node
    :type source: str

    :param target: maya shape node
    :type target: str
    """

    logger.debug("Updating transform node on {} from {}".format(
        target, source))

    # create duplicate of the source transform
    holder = cmds.duplicate(source, parentOnly=True,
                            name="mgear_flex_holder")[0]

    # adds the target shape duplicate into the holder transform node
    cmds.parent(target, holder, add=True, shape=True)

    # unlock locked attributes on holder transform node
    for attr in cmds.listAttr(holder, locked=True) or []:
        cmds.setAttr("{}.{}".format(holder, attr), lock=False)

    # updates the shape
    update_shape(source, target)

    # parents new shape under the correct place
    target_parent = get_parent(target)[0]
    target_parent_parent = get_parent(target_parent)[0]
    cmds.parent(holder, target_parent_parent)
    cmds.delete(target_parent)
    cmds.rename(holder,
                "{}".format(target_parent.split("|")[-1].split(":")[-1]))
def clean_uvs_sets(shape):
    """ Deletes all uv sets besides map1

    This is used to be able to update target shapes with whatever the source
    shape has. This is only relevant for mesh shape types.

    :param shape: The Maya shape node
    :type shape: string
    """

    # check if shape is not a mesh type node
    if cmds.objectType(shape) != "mesh":
        return

    logger.debug("Cleaning uv sets on {}".format(shape))

    # gets uvs indices
    uvs_idx = cmds.getAttr("{}.uvSet".format(shape), multiIndices=True)

    # deletes the extra indices
    for idx in uvs_idx:
        if idx:
            cmds.setAttr("{}.uvSet[{}]".format(shape, idx), lock=False)
            cmds.removeMultiInstance("{}.uvSet[{}]".format(shape, idx))
def create_blendshapes_backup(source, target, nodes):
    """ Creates an updated backup for the given blendshapes nodes on source

    .. important:: This method does not work as the other source/target type
                   of methods in flex. The source is the current geometry
                   before topology update containing the blendshape nodes.
                   We use it in order to create a wrap to the newer target
                   geometry topology.

    :param source: current shape node
    :type source: str

    :param target: new shape node
    :type target: str

    :return: backup blendshape nodes
    :rtype: list
    """

    logger.debug("Creating blendshapes backup")

    # gets simpler shape name
    shape_name = get_prefix_less_name(target)

    # get attributes types
    attrs = get_shape_type_attributes(target)

    # creates source duplicate
    intermediate = get_shape_orig(source)[0]
    source_duplicate = create_duplicate(intermediate, "{}_flex_bs_sourceShape"
                                        .format(shape_name))

    # first loops to create a clean copy of the blendshape nodes
    nodes_copy = []
    for node in nodes:
        duplicate = copy_blendshape_node(node, source_duplicate)
        if duplicate:
            nodes_copy.append(duplicate)

    # creates wrapped target shape
    warp_target = create_duplicate(target, "{}_flex_bs_warpShape"
                                   .format(shape_name))

    # wraps the duplicate to the source
    create_wrap(source_duplicate, warp_target)

    # creates blendshape target shape
    target_duplicate = create_duplicate(target, "{}_flex_bs_targetShape"
                                        .format(shape_name))

    return_nodes = []

    # loops on the blendshape nodes
    for node in nodes_copy:
        # creates transfer blendshape
        transfer_node = cmds.deformer(target_duplicate, type="blendShape",
                                      name="flex_transfer_{}".format(node))[0]

        # get blendshape targets indices. We skip verification because at this
        # stage the copied blendshapes nodes will always have targets
        targets_idx = cmds.getAttr("{}.weight".format(node), multiIndices=True)

        # loop on blendshape targets indices
        for idx in targets_idx or []:
            # input target group attribute
            attr_name = (BLENDSHAPE_TARGET.format(node, idx))

            # blendshape target name
            target_name = cmds.aliasAttr("{}.weight[{}]".format(node, idx),
                                         query=True)

            # loop on actual targets and in-between targets
            for target in cmds.getAttr(attr_name, multiIndices=True):

                # gets and sets the blendshape weight value
                weight = float((target - 5000) / 1000.0)
                cmds.setAttr("{}.weight[{}]".format(node, idx), weight)

                # geometry target attribute
                geometry_target_attr = "{}[{}].inputGeomTarget".format(
                    attr_name, target)

                shape_target = geometry_target_attr.replace(
                    geometry_target_attr.split(".")[0], transfer_node)

                # updates the target
                cmds.connectAttr("{}.{}".format(warp_target, attrs["output"]),
                                 shape_target, force=True)

                cmds.disconnectAttr("{}.{}"
                                    .format(warp_target, attrs["output"]),
                                    shape_target)

                cmds.setAttr("{}.weight[{}]".format(node, idx), 0)

            cmds.setAttr("{}.weight[{}]".format(transfer_node, idx), 0)

            if target_name:
                cmds.aliasAttr(target_name, "{}.weight[{}]".format(
                    transfer_node, idx))

        # adds blendshape node to nodes to return
        return_nodes.append("{}".format(transfer_node))

    # deletes backup process shapes
    cmds.delete(cmds.listRelatives(source_duplicate, parent=True),
                cmds.listRelatives(warp_target, parent=True))

    # forces refresh
    cmds.refresh()

    return return_nodes
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
Beispiel #23
0
def update_rig(source, target, options):
    """ Updates all shapes from the given source group to the target group

    :param source: maya transform node
    :type source: str

    :param target: maya transform node
    :type target: str

    :param options: update options
    :type options: dict
    """

    # gets the matching shapes
    matching_shapes = get_matching_shapes_from_group(source, target)

    logger.info("-" * 90)
    logger.info("Matching shapes: {}".format(matching_shapes))
    logger.info("-" * 90)

    for shape in matching_shapes:
        logger.debug("-" * 90)
        logger.debug("Updating: {}".format(matching_shapes[shape]))

        if options["deformed"]:
            update_deformed_shape(shape, matching_shapes[shape],
                                  options["mismatched_topologies"])

        if options["transformed"]:
            update_transformed_shape(shape, matching_shapes[shape],
                                     options["hold_transform_values"])

        if options["user_attributes"]:
            update_user_attributes(shape, matching_shapes[shape])

        if options["object_display"]:
            logger.debug("Updating object display attributes on {}".format(
                matching_shapes[shape]))
            update_maya_attributes(shape, matching_shapes[shape],
                                   OBJECT_DISPLAY_ATTRIBUTES)

        if options["component_display"]:
            logger.debug("Updating component display attributes on {}".format(
                matching_shapes[shape]))
            update_maya_attributes(shape, matching_shapes[shape],
                                   COMPONENT_DISPLAY_ATTRIBUTES)

        if options["render_attributes"]:
            logger.debug("Updating render attributes on {}".format(
                matching_shapes[shape]))
            update_maya_attributes(shape, matching_shapes[shape],
                                   RENDER_STATS_ATTRIBUTES)

        if options["plugin_attributes"]:
            update_plugin_attributes(shape, matching_shapes[shape])

    logger.info("-" * 90)
    logger.info("Source missing shapes: {}".format(
        get_missing_shapes_from_group(source, target)))
    logger.info("Target missing shapes: {}".format(
        get_missing_shapes_from_group(target, source)))
    logger.info("-" * 90)