Skip to content

validate_fbx_output_node

ValidateFBXOutputNode

Bases: HoudiniInstancePlugin

Validate the instance Output Node.

This will ensure
  • The Output Node Path is set.
  • The Output Node Path refers to an existing object.
  • The Output Node is a Sop or Obj node.
  • The Output Node has geometry data.
  • The Output Node doesn't include invalid primitive types.
Source code in client/ayon_houdini/plugins/publish/validate_fbx_output_node.py
 13
 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
129
130
131
132
133
134
135
136
137
138
139
140
class ValidateFBXOutputNode(plugin.HoudiniInstancePlugin):
    """Validate the instance Output Node.

    This will ensure:
        - The Output Node Path is set.
        - The Output Node Path refers to an existing object.
        - The Output Node is a Sop or Obj node.
        - The Output Node has geometry data.
        - The Output Node doesn't include invalid primitive types.
    """

    order = pyblish.api.ValidatorOrder
    families = ["fbx"]
    label = "Validate FBX Output Node"
    actions = [SelectROPAction, SelectInvalidAction]

    def process(self, instance):

        invalid = self.get_invalid(instance)
        if invalid:
            nodes = [n.path() for n in invalid]
            raise PublishValidationError(
                "See log for details. "
                "Invalid nodes: {0}".format(nodes),
                title="Invalid output node(s)"
            )

    @classmethod
    def get_invalid(cls, instance):
        output_node = instance.data.get("output_node")

        # Check if The Output Node Path is set and
        #  refers to an existing object.
        if output_node is None:
            rop_node = hou.node(instance.data["instance_node"])
            cls.log.error(
                "Output node in '%s' does not exist. "
                "Ensure a valid output path is set.", rop_node.path()
            )

            return [rop_node]

        # Check if the Output Node is a Sop or an Obj node
        #  also, list all sop output nodes inside as well as
        #  invalid empty nodes.
        all_out_sops = []
        invalid = []

        # if output_node is an ObjSubnet or an ObjNetwork
        if output_node.childTypeCategory() == hou.objNodeTypeCategory():
            for node in output_node.allSubChildren():
                if node.type().name() == "geo":
                    out = get_obj_node_output(node)
                    if out:
                        all_out_sops.append(out)
                    else:
                        invalid.append(node)  # empty_objs
                        cls.log.error(
                            "Geo Obj Node '%s' is empty!",
                            node.path()
                        )
            if not all_out_sops:
                invalid.append(output_node)  # empty_objs
                cls.log.error(
                    "Output Node '%s' is empty!",
                    node.path()
                )

        # elif output_node is an ObjNode
        elif output_node.type().name() == "geo":
            out = get_obj_node_output(output_node)
            if out:
                all_out_sops.append(out)
            else:
                invalid.append(node)  # empty_objs
                cls.log.error(
                    "Output Node '%s' is empty!",
                    node.path()
                )

        # elif output_node is a SopNode
        elif output_node.type().category().name() == "Sop":
            all_out_sops.append(output_node)

        # Then it's a wrong node type
        else:
            cls.log.error(
                "Output node %s is not a SOP or OBJ Geo or OBJ SubNet node. "
                "Instead found category type: %s %s",
                output_node.path(), output_node.type().category().name(),
                output_node.type().name()
            )
            return [output_node]

        # Check if all output sop nodes have geometry
        #  and don't contain invalid prims
        invalid_prim_types = ["VDB", "Volume"]
        for sop_node in all_out_sops:
            # Empty Geometry test
            if not hasattr(sop_node, "geometry"):
                invalid.append(sop_node)  # empty_geometry
                cls.log.error(
                    "Sop node '%s' doesn't include any prims.",
                    sop_node.path()
                )
                continue

            frame = instance.data.get("frameStart", 0)
            geo = sop_node.geometryAtFrame(frame)
            if len(geo.iterPrims()) == 0:
                invalid.append(sop_node)  # empty_geometry
                cls.log.error(
                    "Sop node '%s' doesn't include any prims.",
                    sop_node.path()
                )
                continue

            # Invalid Prims test
            for prim_type in invalid_prim_types:
                if geo.countPrimType(prim_type) > 0:
                    invalid.append(sop_node)  # invalid_prims
                    cls.log.error(
                        "Sop node '%s' includes invalid prims of type '%s'.",
                        sop_node.path(), prim_type
                    )

        if invalid:
            return invalid