Beispiel #1
0
    def process(self, instance):

        # Collect fur settings
        settings = {"nodes": []}

        # Get yeti nodes and their transforms
        yeti_shapes = cmds.ls(instance, type="pgYetiMaya")
        for shape in yeti_shapes:
            shape_data = {
                "transform": None,
                "name": shape,
                "cbId": lib.get_id(shape),
                "attrs": None
            }

            # Get specific node attributes
            attr_data = {}
            for attr in SETTINGS:
                current = cmds.getAttr("%s.%s" % (shape, attr))
                attr_data[attr] = current

            # Get transform data
            parent = cmds.listRelatives(shape, parent=True)[0]
            transform_data = {"name": parent, "cbId": lib.get_id(parent)}

            # Store collected data
            shape_data["attrs"] = attr_data
            shape_data["transform"] = transform_data

            settings["nodes"].append(shape_data)

        instance.data["fursettings"] = settings
Beispiel #2
0
    def collect_member_data(self, member, instance_members):
        """Get all information of the node
        Args:
            member (str): the name of the node to check
            instance_members (set): the collected instance members

        Returns:
            dict

        """

        node, components = (member.rsplit(".", 1) + [None])[:2]

        # Only include valid members of the instance
        if node not in instance_members:
            return

        node_id = lib.get_id(node)
        if not node_id:
            self.log.error("Member '{}' has no attribute 'cbId'".format(node))
            return

        member_data = {"name": node, "uuid": node_id}
        if components:
            member_data["components"] = components

        return member_data
Beispiel #3
0
    def collect_sets(self, instance):
        """Collect all objectSets which are of importance for publishing

        It checks if all nodes in the instance are related to any objectSet
        which need to be

        Args:
            instance (list): all nodes to be published

        Returns:
            dict
        """

        sets = dict()
        for node in instance:
            related_sets = lib.get_related_sets(node)
            if not related_sets:
                continue

            for objset in related_sets:
                if objset in sets:
                    continue

                sets[objset] = {"uuid": lib.get_id(objset), "members": list()}

        return sets
    def get_invalid(cls, instance):
        """Get all nodes which do not match the criteria"""

        invalid = []
        types_to_skip = ["locator"]

        # get asset id
        nodes = instance.data.get("out_hierarchy", instance[:])
        for node in nodes:

            # We only check when the node is *not* referenced
            if cmds.referenceQuery(node, isNodeReferenced=True):
                continue

            # Check if node is a shape as deformers only work on shapes
            obj_type = cmds.objectType(node, isAType="shape")
            if not obj_type:
                continue

            # Skip specific types
            if cmds.objectType(node) in types_to_skip:
                continue

            # Get the current id of the node
            node_id = lib.get_id(node)
            if not node_id:
                invalid.append(node)
                continue

            history_id = lib.get_id_from_history(node)
            if history_id is not None and node_id != history_id:
                invalid.append(node)

        return invalid
Beispiel #5
0
    def get_invalid(cls, instance):

        invalid = []

        # Get all id required nodes
        id_required_nodes = lib.get_id_required_nodes(referenced_nodes=True,
                                                      nodes=instance[:])

        # check ids against database ids
        db_asset_ids = io.find({"type": "asset"}).distinct("_id")
        db_asset_ids = set(str(i) for i in db_asset_ids)

        # Get all asset IDs
        for node in id_required_nodes:
            cb_id = lib.get_id(node)

            # Ignore nodes without id, those are validated elsewhere
            if not cb_id:
                continue

            asset_id = cb_id.split(":", 1)[0]
            if asset_id not in db_asset_ids:
                cls.log.error("`%s` has unassociated asset ID" % node)
                invalid.append(node)

        return invalid
    def get_invalid(cls, instance):
        """Return the member nodes that are invalid"""
        invalid = list()

        asset = instance.data['asset']
        asset_data = io.find_one({
            "name": asset,
            "type": "asset"
        },
                                 projection={"_id": True})
        asset_id = str(asset_data['_id'])

        # We do want to check the referenced nodes as we it might be
        # part of the end product
        for node in instance:

            _id = lib.get_id(node)
            if not _id:
                continue

            node_asset_id = _id.split(":", 1)[0]
            if node_asset_id != asset_id:
                invalid.append(node)

        return invalid
    def get_invalid(cls, instance):
        """Return the member nodes that are invalid"""

        # Check only non intermediate shapes
        # todo: must the instance itself ensure to have no intermediates?
        # todo: how come there are intermediates?
        from maya import cmds
        instance_members = cmds.ls(instance, noIntermediate=True, long=True)

        # Collect each id with their members
        ids = defaultdict(list)
        for member in instance_members:
            object_id = lib.get_id(member)
            if not object_id:
                continue
            ids[object_id].append(member)

        # Take only the ids with more than one member
        invalid = list()
        for _ids, members in ids.iteritems():
            if len(members) > 1:
                cls.log.error("ID found on multiple nodes: '%s'" % members)
                invalid.extend(members)

        return invalid
Beispiel #8
0
    def collect_input_connections(self, instance):
        """Collect the inputs for all nodes in the input_SET"""

        # Get the input meshes information
        input_content = cmds.ls(cmds.sets("input_SET", query=True), long=True)

        # Include children
        input_content += cmds.listRelatives(
            input_content, allDescendents=True, fullPath=True) or []

        # Ignore intermediate objects
        input_content = cmds.ls(input_content, long=True, noIntermediate=True)
        if not input_content:
            return []

        # Store all connections
        connections = cmds.listConnections(
            input_content,
            source=True,
            destination=False,
            connections=True,
            # Only allow inputs from dagNodes
            # (avoid display layers, etc.)
            type="dagNode",
            plugs=True) or []
        connections = cmds.ls(connections, long=True)  # Ensure long names

        inputs = []
        for dest, src in pairwise(connections):
            source_node, source_attr = src.split(".", 1)
            dest_node, dest_attr = dest.split(".", 1)

            # Ensure the source of the connection is not included in the
            # current instance's hierarchy. If so, we ignore that connection
            # as we will want to preserve it even over a publish.
            if source_node in instance:
                self.log.debug("Ignoring input connection between nodes "
                               "inside the instance: %s -> %s" % (src, dest))
                continue

            inputs.append({
                "connections": [source_attr, dest_attr],
                "sourceID": lib.get_id(source_node),
                "destinationID": lib.get_id(dest_node)
            })

        return inputs
Beispiel #9
0
    def get_invalid(cls, instance):
        """Return the member nodes that are invalid"""

        # We do want to check the referenced nodes as it might be
        # part of the end product.
        id_nodes = lib.get_id_required_nodes(referenced_nodes=True,
                                             nodes=instance[:])
        invalid = [n for n in id_nodes if not lib.get_id(n)]

        return invalid
Beispiel #10
0
def create_asset_id_hash(nodes):
    """Create a hash based on cbId attribute value
    Args:
        nodes (list): a list of nodes

    Returns:
        dict
    """
    node_id_hash = defaultdict(list)
    for node in nodes:
        value = cblib.get_id(node)
        if value is None:
            continue

        asset_id = value.split(":")[0]
        node_id_hash[asset_id].append(node)

    return dict(node_id_hash)
Beispiel #11
0
    def collect_attributes_changed(self, instance):
        """Collect all userDefined attributes which have changed

        Each node gets checked for user defined attributes which have been
        altered during development. Each changes gets logged in a dictionary

        [{name: node,
          uuid: uuid,
          attributes: {attribute: value}}]

        Args:
            instance (list): all nodes which will be published

        Returns:
            list
        """

        attributes = []
        for node in instance:

            # Collect changes to "custom" attributes
            node_attrs = get_look_attrs(node)

            self.log.info(
                "Node \"{0}\" attributes: {1}".format(node, node_attrs)
            )

            # Only include if there are any properties we care about
            if not node_attrs:
                continue

            node_attributes = {}
            for attr in node_attrs:
                if not cmds.attributeQuery(attr, node=node, exists=True):
                    continue
                attribute = "{}.{}".format(node, attr)
                node_attributes[attr] = cmds.getAttr(attribute)

            attributes.append({"name": node,
                               "uuid": lib.get_id(node),
                               "attributes": node_attributes})

        return attributes
Beispiel #12
0
    def get_invalid(cls, instance):
        """Get all nodes which do not match the criteria"""

        shapes = cmds.ls(instance[:],
                         dag=True,
                         leaf=True,
                         shapes=True,
                         long=True,
                         noIntermediate=True)

        invalid = []
        for shape in shapes:
            history_id = lib.get_id_from_history(shape)
            if history_id:
                current_id = lib.get_id(shape)
                if current_id != history_id:
                    invalid.append(shape)

        return invalid
    def get_invalid(cls, instance):
        """Get all nodes which do not match the criteria"""

        invalid = []

        out_set = next(x for x in instance if x.endswith("out_SET"))
        members = cmds.sets(out_set, query=True)
        shapes = cmds.ls(members,
                         dag=True,
                         leaf=True,
                         shapes=True,
                         long=True,
                         noIntermediate=True)

        for shape in shapes:
            history_id = lib.get_id_from_history(shape)
            if history_id:
                current_id = lib.get_id(shape)
                if current_id != history_id:
                    invalid.append(shape)

        return invalid
Beispiel #14
0
    def update(self, container, representation):

        namespace = container["namespace"]
        container_node = container["objectName"]
        path = api.get_representation_path(representation)
        # Get all node data
        fname, ext = os.path.splitext(path)
        settings_fname = "{}.fursettings".format(fname)
        with open(settings_fname, "r") as fp:
            settings = json.load(fp)

        # Collect scene information of asset
        set_members = cmds.sets(container["objectName"], query=True)
        container_root = lib.get_container_transforms(container,
                                                      members=set_members,
                                                      root=True)
        scene_nodes = cmds.ls(set_members, type="pgYetiMaya", long=True)

        # Build lookup with cbId as keys
        scene_lookup = defaultdict(list)
        for node in scene_nodes:
            cb_id = lib.get_id(node)
            scene_lookup[cb_id].append(node)

        # Re-assemble metadata with cbId as keys
        meta_data_lookup = {n["cbId"]: n for n in settings["nodes"]}

        # Compare look ups and get the nodes which ar not relevant any more
        to_delete_lookup = {
            cb_id
            for cb_id in scene_lookup.keys() if cb_id not in meta_data_lookup
        }
        if to_delete_lookup:

            # Get nodes and remove entry from lookup
            to_remove = []
            for _id in to_delete_lookup:
                # Get all related nodes
                shapes = scene_lookup[_id]
                # Get the parents of all shapes under the ID
                transforms = cmds.listRelatives(
                    shapes, parent=True, fullPath=True) or []
                to_remove.extend(shapes + transforms)

                # Remove id from look uop
                scene_lookup.pop(_id, None)

            cmds.delete(to_remove)

        for cb_id, data in meta_data_lookup.items():

            # Update cache file name
            file_name = data["name"].replace(":", "_")
            cache_file_path = "{}.%04d.fur".format(file_name)
            data["attrs"]["cacheFileName"] = os.path.join(
                path, cache_file_path)

            if cb_id not in scene_lookup:

                self.log.info("Creating new nodes ..")

                new_nodes = self.create_nodes(namespace, [data])
                cmds.sets(new_nodes, addElement=container_node)
                cmds.parent(new_nodes, container_root)

            else:
                # Update the matching nodes
                scene_nodes = scene_lookup[cb_id]
                lookup_result = meta_data_lookup[cb_id]["name"]

                # Remove namespace if any (e.g.: "character_01_:head_YNShape")
                node_name = lookup_result.rsplit(":", 1)[-1]

                for scene_node in scene_nodes:

                    # Get transform node, this makes renaming easier
                    transforms = cmds.listRelatives(
                        scene_node, parent=True, fullPath=True) or []
                    assert len(transforms) == 1, "This is a bug!"

                    # Get scene node's namespace and rename the transform node
                    lead = scene_node.rsplit(":", 1)[0]
                    namespace = ":{}".format(lead.rsplit("|")[-1])

                    new_shape_name = "{}:{}".format(namespace, node_name)
                    new_trans_name = new_shape_name.rsplit("Shape", 1)[0]

                    transform_node = transforms[0]
                    cmds.rename(transform_node,
                                new_trans_name,
                                ignoreShape=False)

                    # Get the newly named shape node
                    yeti_nodes = cmds.listRelatives(new_trans_name,
                                                    children=True)
                    yeti_node = yeti_nodes[0]

                    for attr, value in data["attrs"].items():
                        lib.set_attribute(attr, value, yeti_node)

        cmds.setAttr("{}.representation".format(container_node),
                     str(representation["_id"]),
                     typ="string")
Beispiel #15
0
    def update(self, container, representation):

        io.install()
        namespace = container["namespace"]
        container_node = container["objectName"]

        fur_settings = io.find_one({
            "parent": representation["parent"],
            "name": "fursettings"
        })

        pprint({"parent": representation["parent"], "name": "fursettings"})
        pprint(fur_settings)
        assert fur_settings is not None, (
            "cannot find fursettings representation")

        settings_fname = api.get_representation_path(fur_settings)
        path = api.get_representation_path(representation)
        # Get all node data
        with open(settings_fname, "r") as fp:
            settings = json.load(fp)

        # Collect scene information of asset
        set_members = cmds.sets(container["objectName"], query=True)
        container_root = lib.get_container_transforms(container,
                                                      members=set_members,
                                                      root=True)
        scene_nodes = cmds.ls(set_members, type="pgYetiMaya", long=True)

        # Build lookup with cbId as keys
        scene_lookup = defaultdict(list)
        for node in scene_nodes:
            cb_id = lib.get_id(node)
            scene_lookup[cb_id].append(node)

        # Re-assemble metadata with cbId as keys
        meta_data_lookup = {n["cbId"]: n for n in settings["nodes"]}

        # Compare look ups and get the nodes which ar not relevant any more
        to_delete_lookup = {
            cb_id
            for cb_id in scene_lookup.keys() if cb_id not in meta_data_lookup
        }
        if to_delete_lookup:

            # Get nodes and remove entry from lookup
            to_remove = []
            for _id in to_delete_lookup:
                # Get all related nodes
                shapes = scene_lookup[_id]
                # Get the parents of all shapes under the ID
                transforms = cmds.listRelatives(
                    shapes, parent=True, fullPath=True) or []
                to_remove.extend(shapes + transforms)

                # Remove id from look uop
                scene_lookup.pop(_id, None)

            cmds.delete(to_remove)

        # replace frame in filename with %04d
        RE_frame = re.compile(r"(\d+)(\.fur)$")
        file_name = re.sub(RE_frame, r"%04d\g<2>", os.path.basename(path))
        for cb_id, data in meta_data_lookup.items():

            # Update cache file name
            data["attrs"]["cacheFileName"] = os.path.join(
                os.path.dirname(path), file_name)

            if cb_id not in scene_lookup:

                self.log.info("Creating new nodes ..")

                new_nodes = self.create_nodes(namespace, [data])
                cmds.sets(new_nodes, addElement=container_node)
                cmds.parent(new_nodes, container_root)

            else:
                # Update the matching nodes
                scene_nodes = scene_lookup[cb_id]
                lookup_result = meta_data_lookup[cb_id]["name"]

                # Remove namespace if any (e.g.: "character_01_:head_YNShape")
                node_name = lookup_result.rsplit(":", 1)[-1]

                for scene_node in scene_nodes:

                    # Get transform node, this makes renaming easier
                    transforms = cmds.listRelatives(
                        scene_node, parent=True, fullPath=True) or []
                    assert len(transforms) == 1, "This is a bug!"

                    # Get scene node's namespace and rename the transform node
                    lead = scene_node.rsplit(":", 1)[0]
                    namespace = ":{}".format(lead.rsplit("|")[-1])

                    new_shape_name = "{}:{}".format(namespace, node_name)
                    new_trans_name = new_shape_name.rsplit("Shape", 1)[0]

                    transform_node = transforms[0]
                    cmds.rename(transform_node,
                                new_trans_name,
                                ignoreShape=False)

                    # Get the newly named shape node
                    yeti_nodes = cmds.listRelatives(new_trans_name,
                                                    children=True)
                    yeti_node = yeti_nodes[0]

                    for attr, value in data["attrs"].items():
                        # handle empty attribute strings. Those are reported
                        # as None, so their type is NoneType and this is not
                        # supported on attributes in Maya. We change it to
                        # empty string.
                        if value is None:
                            value = ""
                        lib.set_attribute(attr, value, yeti_node)

        cmds.setAttr("{}.representation".format(container_node),
                     str(representation["_id"]),
                     typ="string")
Beispiel #16
0
    def process_reference(self, context, name=None, namespace=None, data=None):

        import maya.cmds as cmds
        from avalon import maya

        # get roots of selected hierarchies
        selected_roots = []
        for sel in cmds.ls(sl=True, long=True):
            selected_roots.append(sel.split("|")[1])

        # get all objects under those roots
        selected_hierarchy = []
        for root in selected_roots:
            selected_hierarchy.append(
                cmds.listRelatives(root, allDescendents=True) or [])

        # flatten the list and filter only shapes
        shapes_flat = []
        for root in selected_hierarchy:
            shapes = cmds.ls(root, long=True, type="mesh") or []
            for shape in shapes:
                shapes_flat.append(shape)

        # create dictionary of cbId and shape nodes
        scene_lookup = defaultdict(list)
        for node in shapes_flat:
            cb_id = lib.get_id(node)
            scene_lookup[cb_id] = node

        # load rig
        with maya.maintained_selection():
            nodes = cmds.file(self.fname,
                              namespace=namespace,
                              reference=True,
                              returnNewNodes=True,
                              groupReference=True,
                              groupName="{}:{}".format(namespace, name))

        # for every shape node we've just loaded find matching shape by its
        # cbId in selection. If found outMesh of scene shape will connect to
        # inMesh of loaded shape.
        for destination_node in nodes:
            source_node = scene_lookup[lib.get_id(destination_node)]
            if source_node:
                self.log.info("found: {}".format(source_node))
                self.log.info(
                    "creating connection to {}".format(destination_node))

                cmds.connectAttr("{}.outMesh".format(source_node),
                                 "{}.inMesh".format(destination_node),
                                 force=True)

        groupName = "{}:{}".format(namespace, name)

        presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
        colors = presets['plugins']['maya']['load']['colors']

        c = colors.get('yetiRig')
        if c is not None:
            cmds.setAttr(groupName + ".useOutlinerColor", 1)
            cmds.setAttr(groupName + ".outlinerColor", c[0], c[1], c[2])
        self[:] = nodes

        return nodes