0
$\begingroup$

I am creating a mesh that requires assigning individual quads to different textures. So far, I have been creating a material for each texture I want to use, assigning a diffuse shader, changing roughness value, changing interpolation method, setting the color to "Image Texture", and assigning an image file. Doing this for 30 different textures is definitely doable by hand, but it's incredibly tedious and I was wondering if there's an easier way to get this done.

I know there's an addon that lets you import images as planes, but it doesn't let you import as a Diffuse BSDF which I would prefer.

$\endgroup$
1
  • 1
    $\begingroup$ a little script could help here a lot. But i don't know whether you know python.... $\endgroup$
    – Chris
    Oct 17, 2021 at 4:56

1 Answer 1

1
$\begingroup$

I think the only way is scripting. Here's a small script that will automate it. Put it in the text editor and hit run or install it as an addon. Then go to File > Import > Images as Diffuse and pick the images you want to import (use Shift and Ctrl to pick multiple files). It will also (optionally) add material slots for the new materials to your selected objects.

If you import the same image multiple times, it will reuse the same material. It does this by reusing the material with the same file name, so make sure your images have unique names.

If you want to change the nodes it creates, you'll have to edit the script yourself.

# Tested with Blender 2.93
bl_info = {
    "name": "Import Images as Diffuse",
    "blender": (2, 93, 0),
    "category": "Import-Export",
    "location": "File > Import",
}

import bpy
import os


def do_import(op, context, filepaths):
    mats = []
    for path in filepaths:
        mat = import_material(path)
        if mat:
            mats.append(mat)
        else:
            op.report({'WARNING'}, "Couldn't create material: %s" % path)

    if op.add_material_slots:
        for ob in context.selected_objects:
            add_material_slots(ob, mats)

    # Add fake users if needed so materials aren't deleted
    for mat in mats:
        if mat.users == 0:
            mat.use_fake_user = True

    return {'FINISHED'}


def import_material(path):
    name = os.path.basename(path)
    if not name: return None

    # Use existing material with same name if it exists
    mat = bpy.data.materials.get(name)
    if mat: return mat

    try:
        img = bpy.data.images.load(path, check_existing=True)
    except Exception as e:
        print(e)
        return None

    mat = bpy.data.materials.new(name)
    mat.use_nodes = True
    out = mat.node_tree.nodes["Material Output"]
    mat.node_tree.nodes.remove(mat.node_tree.nodes["Principled BSDF"])

    dif = mat.node_tree.nodes.new("ShaderNodeBsdfDiffuse")
    dif.location = (60, 300)
    dif.inputs["Roughness"].default_value = 1.0

    tex = mat.node_tree.nodes.new("ShaderNodeTexImage")
    tex.location = (-260, 300)
    tex.interpolation = 'Closest'
    tex.image = img

    mat.node_tree.links.new(out.inputs[0], dif.outputs[0])
    mat.node_tree.links.new(dif.inputs["Color"], tex.outputs["Color"])

    return mat


def add_material_slots(ob, mats):
    if not hasattr(ob.data, "materials"): return

    for mat in mats:
        if mat.name not in ob.material_slots:
            ob.data.materials.append(mat)


from bpy_extras.io_utils import ImportHelper
from bpy.props import BoolProperty, CollectionProperty
from bpy.types import Operator


class ImportImagesAsDiffuse(Operator, ImportHelper):
    """Create diffuse materials for images"""
    bl_idname = "import_image.to_diffuse"
    bl_label = "Import Images As Diffuse"

    files: CollectionProperty(
        name="File Path",
        type=bpy.types.OperatorFileListElement,
    )

    add_material_slots: BoolProperty(
        name="Add Material Slots",
        description="Add material slots for imported images to selected objects",
        default=True,
    )

    def execute(self, context):
        if self.files:
            dirname = os.path.dirname(self.filepath)
            files = [os.path.join(dirname, file.name) for file in self.files]
        else:
            files = [self.filepath]
        return do_import(self, context, files)


def menu_func_import(self, context):
    self.layout.operator(
        ImportImagesAsDiffuse.bl_idname, text="Images As Diffuse", icon='MATERIAL')


def register():
    bpy.utils.register_class(ImportImagesAsDiffuse)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)


def unregister():
    bpy.utils.unregister_class(ImportImagesAsDiffuse)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)


if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .