Ejemplo n.º 1
0
def upgradeToNodeMaterial(material: bpy.types.Material):
    # the material might not have a node_tree, yet
    if material.node_tree is None and not material.use_nodes:
        material.use_nodes = True
        material.use_nodes = False  # retain the original setting in case the following raises an exception

    matInfoBefore = SEMaterialInfo(material)
    createMaterialNodeTree(material.node_tree)
    matInfo = SEMaterialInfo(material)

    imagesToSet = {
        k: imageFromFilePath(v)
        for k, v in matInfoBefore.images.items()
    }

    # for texType in [TextureType.ColorMetal, TextureType.Diffuse]:
    for texType in [TextureType.ColorMetal]:
        if texType in matInfoBefore.images:
            for mTexType, mTexFileName in matchingFileNamesFromFilePath(
                    matInfoBefore.images[texType]).items():
                if not mTexType in imagesToSet:
                    imagesToSet[mTexType] = imageFromFilePath(
                        mTexFileName.filepath)

    for texType, node in imageNodes(material.node_tree.nodes).items():
        if not node.image and texType in imagesToSet:
            node.image = imagesToSet[texType]

    material.use_nodes = True
Ejemplo n.º 2
0
def sfxToBlender(material: bpy.types.Material, bAlpha: bool):
    if bAlpha:
        shaderTech2 = material.node_tree.nodes["Shader Tech2 Alpha"]
        material.blend_method = 'HASHED'
    else:
        shaderTech2 = material.node_tree.nodes["Shader Tech2"]
        material.blend_method = 'OPAQUE'

    shaderTech2Color(shaderTech2, "Colour", material, "Color")
    shaderTech2Value(shaderTech2, "Specular", material, "Specular")

    if not bAlpha:
        shaderTech2Value(shaderTech2, "Blend Mode G", material, "BlendMode_G")
        shaderTech2Value(shaderTech2, "Ignore Base Colour G", material,
                         "IgnoreBaseColor_G")
        shaderTech2Color(shaderTech2, "Shadow Colour G", material,
                         "ShadowColor_G")
        shaderTech2Color(shaderTech2, "Highlight Colour G", material,
                         "HighlightColor_G")
        shaderTech2Value(shaderTech2, "Blend Sharpness G", material,
                         "BlendSharpness_G")
        shaderTech2Value(shaderTech2, "Blend Level G", material,
                         "BlendLevel_G")
        shaderTech2Value(shaderTech2, "Specular G", material, "Specular_G")

        shaderTech2Value(shaderTech2, "Blend Mode B", material, "BlendMode_B")
        shaderTech2Value(shaderTech2, "Ignore Base Colour B", material,
                         "IgnoreBaseColor_B")
        shaderTech2Color(shaderTech2, "Shadow Colour B", material,
                         "ShadowColor_B")
        shaderTech2Color(shaderTech2, "Highlight Colour B", material,
                         "HighlightColor_B")
        shaderTech2Value(shaderTech2, "Blend Sharpness B", material,
                         "BlendSharpness_B")
        shaderTech2Value(shaderTech2, "Blend Level B", material,
                         "BlendLevel_B")
        shaderTech2Value(shaderTech2, "Specular B", material, "Specular_B")

    shaderTech2.inputs["Game Lighting"].default_value = 1

    setTextureFromCustomProperties(material, "Base Texture", "SfxTextureBase")
    if bAlpha:
        setTextureFromCustomProperties(material, "Specular Texture",
                                       "SfxTextureSpecular")
    else:
        setTextureFromCustomProperties(material, "Overlay Texture",
                                       "SfxTextureOverlay")
    setTextureFromCustomProperties(material, "Light Map Texture",
                                   "SfxTextureLightmap")
    setEnvTextureFromCustomProperties(material, "Environment Texture",
                                      "SkyboxTexture")

    if material.node_tree.nodes["Environment Texture"].image:
        shaderTech2.inputs["Game Lighting"].default_value = 1
    else:
        shaderTech2.inputs["Game Lighting"].default_value = 0
def new_material_variant(
        material: bpy.types.Material, name: str,
        image_paths: List[Path]) -> Optional[bpy.types.Material]:
    """Duplicate the material and replace images by new images from disk.

    The provided images must only differ in the variant tag in order to match existing image node file-paths.

    :param material: Material to create a variant of.
    :type material: bpy.types.Material
    :param name: Name for the new material.
    :type name: str
    :param image_paths: Paths to image files to replace existing images.
    :type image_paths: List[Path]
    :return: The new material or None, if no images were replaced.
    :rtype: Optional[bpy.types.Material]
    """
    mat = material.copy()
    mat.name = name
    img_nodes = get_img_nodes(mat)
    was_replaced = False
    for node in img_nodes:
        try:
            img_name = bpy.path.basename(node.image.filepath)
        except AttributeError:
            continue
        var_path = fops.find_variant_path(img_name, image_paths)
        if var_path:
            replace_img(node, var_path)
            was_replaced |= True
    if not was_replaced:
        if mat.users == 0:
            bpy.data.materials.remove(mat)
        del mat
        return None
    return mat
Ejemplo n.º 4
0
def make_fish_colored_transparent(material: bpy.types.Material):
    assert isinstance(material, bpy.types.Material)
    material.use_nodes = True  # Enable node mode
    check_default_fish_node_tree(material)
    utils.clear_all_node_links(material)

    nodes, links = material.node_tree.nodes, material.node_tree.links
    out = nodes["Material Output"]
    img = nodes["Image Texture"]
    glossy = nodes["Glossy BSDF"]
    mix = nodes.new("ShaderNodeMixShader")
    transparent = nodes.new("ShaderNodeBsdfTransparent")
    mix.inputs["Fac"].default_value = cng.DEFAULT_MIXSHADER_FAC
    transparent.inputs[
        "Color"].default_value = cng.DEFAULT_ALTER_COLOR  # R G B A
    glossy.inputs["Roughness"].default_value = 0.85

    links.new(input=glossy.inputs[0],
              output=img.outputs[0])  # Connect img colors to glossy color
    links.new(input=mix.inputs[2],
              output=transparent.outputs[0])  # Connect Transparent to mix
    links.new(input=mix.inputs[1],
              output=glossy.outputs[0])  # Connect Glossy to mix
    links.new(input=out.inputs[0], output=mix.outputs[0])  # Mix to out
    return material
def import_xml_material(material: bpy.types.Material, name: str, xml_path: str,
                        copy_textures: bool):
    """ Create RPR material at current material slot using xml.
    Nodes tree is cleaned if present, new created otherwise.
    New output node added if no output found.
    Change Blender material name to material library name.
    Copy textures locally if requested."""
    def clean_material_tree():
        """ Remove every node from material tree except for output """
        log("Cleaning material nodes tree")
        material.node_tree.nodes.clear()

    def create_material() -> bpy.types.Material:
        """ Create new material and assign to current empty material slot, create slot if none found """
        if not bpy.context.object.material_slots.keys():
            bpy.ops.object.material_slot_add()
        # 2. create material for it
        new_material = bpy.data.materials.new(name=name)
        # 3. assign material to material slot
        bpy.context.object.material_slots[
            bpy.context.object.active_material_index].material = new_material
        new_material.use_nodes = True

        return new_material

    def create_output_node() -> bpy.types.ShaderNode:
        """ Create and return new output node """
        output_node = material.node_tree.nodes.new('ShaderNodeOutputMaterial')

        log("New output node is {}".format(output_node))
        return output_node

    if not material:
        log("No material tree found, creating new material")
        material = create_material()
    else:
        material.name = name

    # overwrite existing nodes tree
    clean_material_tree()
    output = create_output_node()

    root_folder = rpr_material_library.path
    material_folder = os.path.dirname(xml_path)

    # create images loader
    image_loader = MaterialImageLoader(root_folder, material_folder,
                                       copy_textures)

    # create material by xml
    closure = compile_material_from_xml(xml_path, material.node_tree,
                                        image_loader)

    # Link closure to output node
    if closure:
        log("Linking closure {} to active output {}".format(closure, output))
        material.node_tree.links.new(closure.outputs[0], output.inputs[0])
Ejemplo n.º 6
0
def create_ls3d_material(material: bpy.types.Material) -> None:
    material.use_nodes = True
    props = material.ls3d_props
    ntree = material.node_tree
    ntree.nodes.clear()

    # Output node
    output = ntree.nodes.new(type="ShaderNodeOutputMaterial")
    output.name = NODE_OUTPUT
    output.location = (300, 0)

    # Shader node
    bsdf = ntree.nodes.new(type="ShaderNodeBsdfPrincipled")
    bsdf.name = NODE_SHADER
    bsdf.inputs["Specular"].default_value = 0.0
    ntree.links.new(output.inputs["Surface"], bsdf.outputs["BSDF"])

    # Diffuse TexImage
    diffuse = ntree.nodes.new(type="ShaderNodeTexImage")
    diffuse.name = NODE_DIFFUSE
    diffuse.location = (-300, -80)

    # Alpha TexImage
    alpha = ntree.nodes.new(type="ShaderNodeTexImage")
    alpha.name = NODE_ALPHA
    alpha.location = (-300, -437)

    # Environment TexImage
    environment = ntree.nodes.new(type="ShaderNodeTexImage")
    environment.name = NODE_ENVIRONMENT
    environment.location = (-600, -80)

    # Linking
    if props.diffuse_texture:
        ntree.links.new(bsdf.inputs["Base Color"], diffuse.outputs["Color"])

    if props.alpha_texture and not props.diffuse_alpha:
        ntree.links.new(bsdf.inputs["Alpha"], alpha.outputs["Color"])
        material.blend_method = 'BLEND'
    elif props.diffuse_alpha:
        ntree.links.new(bsdf.inputs["Alpha"], diffuse.outputs["Alpha"])
        material.blend_method = 'BLEND'
Ejemplo n.º 7
0
def create_shader(material: bpy.types.Material):
    bpy.context.object.active_material = material
    material.use_nodes = True
    node_tree = material.node_tree

    # bsdf
    bsdf_node = pickup_node(node_tree, bpy.types.ShaderNodeBsdfPrincipled)
    if bsdf_node is None or bsdf_node.name != "bsdf":
        bsdf_node = node_tree.nodes.new("ShaderNodeBsdfPrincipled")
        bsdf_node.name = "bsdf"
    bsdf_node.location = (0.0, 0.0)

    # output
    output_node = pickup_node(node_tree, bpy.types.ShaderNodeOutputMaterial)
    if output_node is None or output_node.name != "output":
        output_node = node_tree.nodes.new("ShaderNodeOutputMaterial")
        output_node.name = "output"

    node_tree.links.new(bsdf_node.outputs[0], output_node.inputs[0])
    output_node.location = (300.0, 0.0)
Ejemplo n.º 8
0
def make_aov_material_output_node(
    mat: bpy.types.Material = None,
    obj: bpy.types.Object = None,
    style: str = "instance",
) -> None:
    """Make AOV Output nodes in Composition Graph.

    Args:
        mat (bpy.types.Material, optional): A blender material (either it's name or the object itself).
        obj (bpy.types.Object, optional): A blender object (either it's name or the object itself).
        style (str, optional): Type of segmentation in [instance, category]. Defaults to 'instance'.

    Raises:
        ValueError: Invalid style, no object or material given.
    """
    # Make sure engine is set to Cycles
    scene = zpy.blender.verify_blender_scene()
    if not (scene.render.engine == "CYCLES"):
        log.warning(" Setting render engine to CYCLES to use AOV")
        scene.render.engine == "CYCLES"

    # TODO: Refactor this legacy "styles" code

    # Only certain styles are available
    valid_styles = ["instance", "category"]
    assert (
        style in valid_styles
    ), f"Invalid style {style} for AOV material output node, must be in {valid_styles}."

    # HACK: multiple material slots
    all_mats = []

    # Use material
    if mat is not None:
        all_mats = [mat]
    # Get material from object
    elif obj is not None:
        if obj.active_material is None:
            log.debug(f"No active material found for {obj.name}")
            return
        if len(obj.material_slots) > 1:
            for mat in obj.material_slots:
                all_mats.append(mat.material)
        else:
            all_mats.append(obj.active_material)
    else:
        raise ValueError("Must pass in an Object or Material")

    # HACK: multiple material slots
    for mat in all_mats:

        # Make sure material is using nodes
        if not mat.use_nodes:
            mat.use_nodes = True
        tree = mat.node_tree

        # Vertex Color Node
        vcol_node = zpy.nodes.get_or_make(f"{style} Vertex Color",
                                          "ShaderNodeVertexColor", tree)
        vcol_node.layer_name = style

        # AOV Output Node
        # HACK: This type of node has a "name" property which prevents using the
        # normal zpy.nodes code due to a scope conflict with the bpy.types.Node.name property
        # See: https://docs.blender.org/api/current/bpy.types.ShaderNodeOutputAOV.html
        _name = style
        aovout_node = None
        for _node in tree.nodes:
            if _node.name == _name:
                aovout_node = _node
        if aovout_node is None:
            aovout_node = tree.nodes.new("ShaderNodeOutputAOV")
        aovout_node.name = style

        tree.links.new(vcol_node.outputs["Color"], aovout_node.inputs["Color"])
Ejemplo n.º 9
0
def set_yakuza_shader_material_from_attributeset(
        material: bpy.types.Material, yakuza_inputs: bpy.types.NodeInputs,
        attribute_set: GMDAttributeSet, gmd_folder: str):
    """
    Given a material and an attribute set, attach all of the relevant data from the attribute set to the material.
    :param material: The material to update
    :param yakuza_inputs: The inputs to the Yakuza Shader node in the material
    :param attribute_set: The GMDAttributeSet this Material represents.
    :param gmd_folder: The folder to examine for new textures.
    :return: None
    """

    # Setup the yakuza_data inside the material
    material.yakuza_data.inited = True
    material.yakuza_data.shader_name = attribute_set.shader.name
    material.yakuza_data.shader_vertex_layout_flags = f"{attribute_set.shader.vertex_buffer_layout.packing_flags:016x}"
    #print(f"import shader {attribute_set.shader.name} flags {attribute_set.shader.vertex_buffer_layout.packing_flags} layout {attribute_set.shader.vertex_buffer_layout}")
    material.yakuza_data.attribute_set_flags = f"{attribute_set.attr_flags:016x}"
    material.yakuza_data.unk12 = attribute_set.unk12.float_data if attribute_set.unk12 else [
        0
    ] * 32
    material.yakuza_data.unk14 = attribute_set.unk14.int_data if attribute_set.unk14 else [
        0
    ] * 32
    material.yakuza_data.attribute_set_floats = attribute_set.attr_extra_properties
    material.yakuza_data.material_origin_type = attribute_set.material.origin_version.value
    material.yakuza_data.material_json = json.dumps(
        vars(attribute_set.material.origin_data))

    # Set the skin shader to 1 if the shader is a skin shader
    yakuza_inputs[
        "Skin Shader"].default_value = 1.0 if "[skin]" in attribute_set.shader.name else 0.0

    # Convenience function for creating a texture node for an Optional texture
    def set_texture(
        set_into: NodeSocketColor,
        tex_name: Optional[str],
        next_image_y: int = 0,
        color_if_not_found=(1, 0, 1, 1)
    ) -> Tuple[Optional[ShaderNodeTexImage], int]:
        if not tex_name:
            return None, next_image_y
        image_node = load_texture_from_name(material.node_tree, gmd_folder,
                                            tex_name, color_if_not_found)
        image_node.location = (-500, next_image_y)
        #image_node.label = tex_name
        image_node.hide = True
        material.node_tree.links.new(image_node.outputs["Color"], set_into)
        next_image_y -= 100
        return image_node, next_image_y

    # Create the diffuse texture
    diffuse_tex, next_y = set_texture(yakuza_inputs["texture_diffuse"],
                                      attribute_set.texture_diffuse)
    if diffuse_tex and re.search(r'^s_b', attribute_set.shader.name):
        # The shader name starts with s_b so we know it's transparent
        # Link the texture alpha with the Yakuza Shader, and make the material do alpha blending.
        material.node_tree.links.new(diffuse_tex.outputs["Alpha"],
                                     yakuza_inputs["Diffuse Alpha"])
        material.blend_method = "BLEND"
    # Attach the other textures.
    _, next_y = set_texture(yakuza_inputs["texture_multi"],
                            attribute_set.texture_multi, next_y,
                            DEFAULT_MULTI_COLOR)
    _, next_y = set_texture(yakuza_inputs["texture_normal"],
                            attribute_set.texture_normal, next_y,
                            DEFAULT_NORMAL_COLOR)
    _, next_y = set_texture(yakuza_inputs["texture_refl"],
                            attribute_set.texture_refl, next_y)
    _, next_y = set_texture(yakuza_inputs["texture_unk1"],
                            attribute_set.texture_unk1, next_y)
    _, next_y = set_texture(yakuza_inputs["texture_rs"],
                            attribute_set.texture_rs, next_y)
    _, next_y = set_texture(yakuza_inputs["texture_rt"],
                            attribute_set.texture_rt, next_y)
    _, next_y = set_texture(yakuza_inputs["texture_rd"],
                            attribute_set.texture_rd, next_y)
Ejemplo n.º 10
0
 def material_init(mat: bpy.types.Material) -> None:
     mat.use_nodes = True
     for node in mat.node_tree.nodes:
         if node.type != "OUTPUT_MATERIAL":
             mat.node_tree.nodes.remove(node)
Ejemplo n.º 11
0
def make_aov_material_output_node(
    mat: bpy.types.Material = None,
    obj: bpy.types.Object = None,
    style: str = 'instance',
) -> None:
    """ Make AOV Output nodes in Composition Graph. """
    # Make sure engine is set to Cycles
    if not (bpy.context.scene.render.engine == "CYCLES"):
        log.warning(' Setting render engine to CYCLES to use AOV')
        bpy.context.scene.render.engine == "CYCLES"
    # Only certain styles are available
    valid_styles = ['instance', 'category']
    assert style in valid_styles, \
        f'Invalid style {style} for AOV material output node, must be in {valid_styles}.'

    # HACK: multiple material slots
    all_mats = []

    # Use material
    if mat is not None:
        all_mats = [mat]
    # Get material from object
    elif obj is not None:
        if obj.active_material is None:
            log.debug(f'No active material found for {obj.name}')
            return
        if len(obj.material_slots) > 1:
            for mat in obj.material_slots:
                all_mats.append(mat.material)
        else:
            all_mats.append(obj.active_material)
    else:
        raise ValueError('Must pass in an Object or Material')

    # HACK: multiple material slots
    for mat in all_mats:

        # Make sure material is using nodes
        if not mat.use_nodes:
            mat.use_nodes = True
        _tree = mat.node_tree

        # Vertex Color Node
        _name = f'{style} Vertex Color'
        vertexcolor_node = _tree.nodes.get(_name)
        if vertexcolor_node is None:
            vertexcolor_node = _tree.nodes.new('ShaderNodeVertexColor')
        vertexcolor_node.layer_name = style
        vertexcolor_node.name = _name

        # AOV Output Node
        _name = style
        # HACK: property "name" of ShaderNodeOutputAOV behaves strangely with .get()
        aovoutput_node = None
        for _node in _tree.nodes:
            if _node.name == _name:
                aovoutput_node = _node
        if aovoutput_node is None:
            aovoutput_node = _tree.nodes.new('ShaderNodeOutputAOV')
        aovoutput_node.name = style
        _tree.links.new(vertexcolor_node.outputs['Color'],
                        aovoutput_node.inputs['Color'])
Ejemplo n.º 12
0
def clearShaderNodes(mat: bpy.types.Material) -> None:
    """Clears all the existing shader nodes and node links of the material mat."""

    mat.use_nodes = True
    mat.node_tree.nodes.clear()
    mat.node_tree.links.clear()