Example #1
0
    def add_normal(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks,
                   normal_image_path: str, principled_bsdf: bpy.types.Node,
                   invert_y_channel: bool):
        """
        Adds normal to the principled bsdf node.

        :param nodes: Nodes from the current material
        :param links: Links from the current material
        :param normal_image_path: Path to the metal image
        :param principled_bsdf: Principled BSDF node of the current material
        :param invert_y_channel: If this is True the Y Color Channel is inverted.
        :return: bpy.types.Node: The newly constructed texture node
        """
        normal_y_value = MaterialLoaderUtility.y_texture_node * -3
        if os.path.exists(normal_image_path):
            normal_texture = MaterialLoaderUtility.create_image_node(
                nodes, normal_image_path, True,
                MaterialLoaderUtility.x_texture_node, normal_y_value)
            if invert_y_channel:

                separate_rgba = nodes.new('ShaderNodeSeparateRGB')
                separate_rgba.location.x = 4.0 / 5.0 * MaterialLoaderUtility.x_texture_node
                separate_rgba.location.y = normal_y_value
                links.new(normal_texture.outputs["Color"],
                          separate_rgba.inputs["Image"])

                invert_node = nodes.new("ShaderNodeInvert")
                invert_node.inputs["Fac"].default_value = 1.0
                invert_node.location.x = 3.0 / 5.0 * MaterialLoaderUtility.x_texture_node
                invert_node.location.y = normal_y_value

                links.new(separate_rgba.outputs["G"],
                          invert_node.inputs["Color"])

                combine_rgba = nodes.new('ShaderNodeCombineRGB')
                combine_rgba.location.x = 2.0 / 5.0 * MaterialLoaderUtility.x_texture_node
                combine_rgba.location.y = normal_y_value
                links.new(separate_rgba.outputs["R"], combine_rgba.inputs["R"])
                links.new(invert_node.outputs["Color"],
                          combine_rgba.inputs["G"])
                links.new(separate_rgba.outputs["B"], combine_rgba.inputs["B"])

                current_output = combine_rgba.outputs["Image"]
            else:
                current_output = normal_texture.outputs["Color"]

            normal_map = nodes.new("ShaderNodeNormalMap")
            normal_map.inputs["Strength"].default_value = 1.0
            normal_map.location.x = 1.0 / 5.0 * MaterialLoaderUtility.x_texture_node
            normal_map.location.y = normal_y_value
            links.new(current_output, normal_map.inputs["Color"])
            links.new(normal_map.outputs["Normal"],
                      principled_bsdf.inputs["Normal"])
            return normal_texture
        return None
Example #2
0
def add_displacement(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks,
                     displacement_image_path: str,
                     output_node: bpy.types.Node):
    """
    Adds bump to the principled bsdf node.

    :param nodes: Nodes from the current material
    :param links: Links from the current material
    :param displacement_image_path: Path to the metal image
    :param output_node: Output node of the current material
    :return: bpy.types.Node: The newly constructed texture node
    """
    if os.path.exists(displacement_image_path):
        displacement_texture = create_image_node(nodes,
                                                 displacement_image_path, True,
                                                 x_texture_node,
                                                 y_texture_node * -4)
        displacement_node = nodes.new("ShaderNodeDisplacement")
        displacement_node.inputs["Midlevel"].default_value = 0.5
        displacement_node.inputs["Scale"].default_value = 0.15
        displacement_node.location.x = x_texture_node * 0.5
        displacement_node.location.y = y_texture_node * -4
        links.new(displacement_texture.outputs["Color"],
                  displacement_node.inputs["Height"])
        links.new(displacement_node.outputs["Displacement"],
                  output_node.inputs["Displacement"])
        return displacement_texture
    return None
Example #3
0
def add_ambient_occlusion(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks,
                          ambient_occlusion_image_path,
                          principled_bsdf: bpy.types.Node,
                          base_color: bpy.types.Node):
    """
    Adds ambient occlusion to the principled bsdf node.

    :param nodes: Nodes from the current material
    :param links: Links from the current material
    :param ambient_occlusion_image_path: Path to the ambient occlusion image
    :param principled_bsdf: Principled BSDF node of the current material
    :param base_color: Base color node of the current material
    :return: bpy.types.Node: The newly constructed texture node
    """
    if os.path.exists(ambient_occlusion_image_path):
        ao_color = create_image_node(nodes, ambient_occlusion_image_path, True,
                                     x_texture_node, y_texture_node * 2)
        math_node = nodes.new(type='ShaderNodeMixRGB')
        math_node.blend_type = "MULTIPLY"
        math_node.location.x = x_texture_node * 0.5
        math_node.location.y = y_texture_node * 1.5
        math_node.inputs["Fac"].default_value = 0.333

        links.new(base_color.outputs["Color"], math_node.inputs[1])
        links.new(ao_color.outputs["Color"], math_node.inputs[2])
        links.new(math_node.outputs["Color"],
                  principled_bsdf.inputs["Base Color"])

        return ao_color
    return None
Example #4
0
def create_image_node(nodes: bpy.types.Nodes,
                      image: Union[str, bpy.types.Image],
                      non_color_mode=False,
                      x_location=0,
                      y_location=0):
    """
    Creates a texture image node inside of a material.

    :param nodes: Nodes from the current material
    :param image: Either the path to the image which should be loaded or the bpy.types.Image
    :param non_color_mode: If this True, the color mode of the image will be "Non-Color"
    :param x_location: X Location in the node tree
    :param y_location: Y Location in the node tree
    :return: bpy.type.Node: Return the newly constructed image node
    """
    image_node = nodes.new('ShaderNodeTexImage')
    if isinstance(image, bpy.types.Image):
        image_node.image = image
    else:
        image_node.image = bpy.data.images.load(image, check_existing=True)
    if non_color_mode:
        image_node.image.colorspace_settings.name = 'Non-Color'
    image_node.location.x = x_location
    image_node.location.y = y_location
    return image_node
Example #5
0
    def add_bump(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks,
                 bump_image_path: str, principled_bsdf: bpy.types.Node):
        """
        Adds bump to the principled bsdf node.

        :param nodes: Nodes from the current material
        :param links: Links from the current material
        :param bump_image_path: Path to the metal image
        :param principled_bsdf: Principled BSDF node of the current material
        :return: bpy.types.Node: The newly constructed texture node
        """
        bump_y_value = MaterialLoaderUtility.y_texture_node * -3
        if os.path.exists(bump_image_path):
            bump_texture = MaterialLoaderUtility.create_image_node(
                nodes, bump_image_path, True,
                MaterialLoaderUtility.x_texture_node, bump_y_value)
            bump_map = nodes.new("ShaderNodeBumpMap")
            bump_map.inputs["Strength"].default_value = 1.0
            bump_map.location.x = 1.0 / 5.0 * MaterialLoaderUtility.x_texture_node
            bump_map.location.y = bump_y_value
            links.new(bump_texture.outputs["Color"],
                      bump_map.inputs["Heights"])
            links.new(bump_map.outputs["Normal"],
                      principled_bsdf.inputs["Normal"])
            return bump_texture
        return None
Example #6
0
def copy_nodes(nodes: bpy.types.Nodes, goal_nodes: bpy.types.Nodes):
    """
    Copies all nodes from the given list into the group with their attributes

    :param: node: the nodes which should be copied
    :param: goal_nodes: the nodes where they should be copied too
    """

    if len(goal_nodes) > 0:
        raise Exception(
            f"This function only works if goal_nodes was empty before, has {len(goal_nodes)} elements."
        )

    # the attributes that should be copied for every link
    input_attributes = ["default_value", "name"]
    output_attributes = ["default_value", "name"]

    for node in nodes:
        # create a new node in the goal_nodes and find and copy its attributes
        new_node = goal_nodes.new(node.bl_idname)
        node_attributes = get_node_attributes(node)
        copy_attributes(node_attributes, node, new_node)

        # copy the attributes for all inputs
        for inp, new_inp in zip(node.inputs, new_node.inputs):
            copy_attributes(input_attributes, inp, new_inp)

        # copy the attributes for all outputs
        for out, new_out in zip(node.outputs, new_node.outputs):
            copy_attributes(output_attributes, out, new_out)
Example #7
0
def create_node(
        nodes: bpy.types.Nodes,
        node_attr_name: str,
        _type: str,
        pos: Iterable[float] = None,
        inputs: dict = None,  # Index (integer or string), value
        outputs: dict = None,  # Index (integer or string), value
        attributes: dict = None) -> None:
    """
    Gets the node with the specified name from the nodes
    Or Creates it if it doesnt exist
    Then updates the inputs, outputs and attributes if necessary
    Then adds it to the MaterialManager for easy access
    """
    # node = nodes.get(node_attr_name)
    # if not node:
    node = nodes.new(type=_type)
    node.name = node_attr_name
    if pos:
        node.location = pos
    if inputs:
        for index, value in inputs.items():
            node.inputs[index].default_value = value
    if outputs:
        for index, value in outputs.items():
            node.outputs[index].default_value = value
    if attributes:
        for name, value in attributes.items():
            setattr(node, name, value)
Example #8
0
    def connect_uv_maps(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks, collection_of_texture_nodes: list):
        """
        Connect all given texture nodes to a newly constructed UV node.

        :param nodes: Nodes from the current material
        :param links: Links from the current material
        :param collection_of_texture_nodes: List of :class: `bpy.type.Node` of type :class: `ShaderNodeTexImage`
        """
        if len(collection_of_texture_nodes) > 0:
            texture_coords = nodes.new("ShaderNodeTexCoord")
            texture_coords.location.x = MaterialLoaderUtility.x_texture_node * 1.4
            mapping_node = nodes.new("ShaderNodeMapping")
            mapping_node.location.x = MaterialLoaderUtility.x_texture_node * 1.2

            links.new(texture_coords.outputs["UV"], mapping_node.inputs["Vector"])
            for texture_node in collection_of_texture_nodes:
                if texture_node is not None:
                    links.new(mapping_node.outputs["Vector"], texture_node.inputs["Vector"])
def get_or_create_node(
        nodes: bpy.types.Nodes,
        node_attr_name: str,
        _type: str,
        pos: Iterable[float] = None,
        inputs: dict = None,  # Index (integer or string), value
        outputs: dict = None,  # Index (integer or string), value
        attributes: dict = None) -> None:
    node = nodes.get(node_attr_name)
    if not node:
        node = nodes.new(type=_type)
    node.name = node_attr_name
    if pos:
        node.location = pos
    if inputs:
        for index, value in inputs.items():
            node.inputs[index].default_value = value
    if outputs:
        for index, value in outputs.items():
            node.outputs[index].default_value = value
    if attributes:
        for name, value in attributes.items():
            setattr(node, name, value)
    setattr(MaterialManager, node_attr_name, node)
Example #10
0
def clean_nodes(nodes: bpy.types.Nodes) -> None:
    for node in nodes:
        nodes.remove(node)