Skip to content

validate_excluded_parents_visible

ValidateExcludedParentsVisible

Bases: MayaInstancePlugin, OptionalPyblishPluginMixin

Validate whether all parents are visible in frame range when 'include parent hierarchy' is disabled for the instance.

This validation helps to detect the issue where an animator may have hidden or keyed visibilities on parent nodes for an export where these parents are not included in the export. Because if so, those invisibilities would not be included in the export either, giving a different visual result than what the artist likely intended in their workfile

Source code in client/ayon_maya/plugins/publish/validate_excluded_parents_visible.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class ValidateExcludedParentsVisible(plugin.MayaInstancePlugin,
                                     OptionalPyblishPluginMixin):
    """Validate whether all parents are visible in frame range when 'include
    parent hierarchy' is disabled for the instance.

    This validation helps to detect the issue where an animator may have hidden
    or keyed visibilities on parent nodes for an export where these parents
    are not included in the export. Because if so, those invisibilities would
    not be included in the export either, giving a different visual result than
    what the artist likely intended in their workfile

    """

    order = pyblish.api.ValidatorOrder
    families = ["pointcache", "animation"]
    label = "Excluded parents visible"
    actions = [ayon_maya.api.action.SelectInvalidAction]

    @classmethod
    def get_invalid(cls, instance):

        # Only validate if we exclude parent hierarchy
        if instance.data.get("includeParentHierarchy", True):
            return []

        if "out_hierarchy" in instance.data:
            # Animation instances
            members = instance.data["out_hierarchy"]
        else:
            members = instance.data["setMembers"]

        members = cmds.ls(members, type="dagNode", long=True)  # DAG nodes only
        if not members:
            cls.log.debug("No members found in instance.")
            return []

        roots = lib.get_highest_in_hierarchy(members)

        # If there are no parents to the root we are already including the
        # full hierarchy, so we can skip checking visibilities on parents
        parents = cmds.listRelatives(roots, parent=True, fullPath=True)
        if not parents:
            return []

        # Include ancestors to check for visibilities on them
        ancestors = list(parents)
        for parent in parents:
            ancestors.extend(lib.iter_parents(parent))

        # Check if the parent is hidden anywhere within the frame range
        invalid = []
        frame_start = int(instance.data["frameStartHandle"])
        frame_end = int(instance.data["frameEndHandle"])

        cls.log.debug(
            "Validating invisibilities for excluded ancestors in frame "
            f"range {frame_start}-{frame_end}: {ancestors}.")
        for ancestor in ancestors:
            attr = f"{ancestor}.visibility"

            # We need to check whether the ancestor is ever invisible
            # during the frame range if it has inputs
            has_inputs = bool(cmds.listConnections(
                attr, source=True, destination=False))
            if has_inputs:
                for frame in range(frame_start, frame_end+1):
                    if cmds.getAttr(attr, time=frame):
                        continue

                    # We found an invisible frame
                    cls.log.warning(
                        "Excluded parent is invisible on frame "
                        f"{frame}: {ancestor}")
                    invalid.append(ancestor)
                    break

            # If no inputs, check the current visibility
            elif not cmds.getAttr(attr):
                cls.log.warning(f"Excluded parent is invisible: {ancestor}")
                invalid.append(ancestor)

        return invalid

    def process(self, instance):
        """Process all the nodes in the instance 'objectSet'"""
        if not self.is_active(instance.data):
            return

        invalid = self.get_invalid(instance)
        if invalid:
            invalid_list = "\n".join(f"- {node}" for node in invalid)

            raise PublishValidationError(
                "Invisible parents found that are excluded from the export:\n"
                "{0}".format(invalid_list),
                title="Excluded parents are invisible",
                description=self.get_description()
            )

    @staticmethod
    def get_description():
        return inspect.cleandoc("""### Excluded parents are invisible

        The instance is set to exclude the parent hierarchy, however the
        excluded parents are invisible within the exported frame range.
        This may be on all frames, of if animated on only certain frames.

        Because the export excludes those parents the exported geometry will
        **not** have these (animated) invisibilities and will appear visible
        in the output regardless of how your scene looked on export.

        To resolve this, either move the invisibility down into the hierarchy
        that you are including in the export. Or, export with include parent
        hierarchy enabled.
        """)

process(instance)

Process all the nodes in the instance 'objectSet'

Source code in client/ayon_maya/plugins/publish/validate_excluded_parents_visible.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def process(self, instance):
    """Process all the nodes in the instance 'objectSet'"""
    if not self.is_active(instance.data):
        return

    invalid = self.get_invalid(instance)
    if invalid:
        invalid_list = "\n".join(f"- {node}" for node in invalid)

        raise PublishValidationError(
            "Invisible parents found that are excluded from the export:\n"
            "{0}".format(invalid_list),
            title="Excluded parents are invisible",
            description=self.get_description()
        )