本日は Blender の技術調査枠です。
Blender2.8で利用可能なpythonスクリプトを作ります。
類似マテリアルスロットの差し替え
マテリアルスロットに登録されているマテリアル同士を比較し、類似するマテリアルがあれば差し替えます。
本スクリプトでは以下の条件で類似マテリアルを判断しています。
1.指定マテリアルのアクティブな出力ノードにノードが接続されているか
2.アクティブな出力ノードに接続されたノードはプリンシプルBSDFか
3.プリンシプルBSDFのベースカラー端子にリンクが貼られておらず、デフォルト値が有効か
4.デフォルト値が有効な場合、そのカラー値が一致すれば類似と判断する
スクリプトはBlenderプロジェクトフォルダにある2つのPythonスクリプトを参照しています。
・Script_material_marge_object.py
# 各種ライブラリインポート import sys, os import bpy ## エディタ実行時に追加ライブラリを参照するため ## 読み込みスクリプトのディレクトリをシステムパスに追加する # Blenderプロジェクトのファイルパスを取得 blend_filepath = bpy.data.filepath # Blenderプロジェクトの読み込み元のディレクトリパスを取得 blend_dirpath = os.path.dirname(blend_filepath) # 読み込み元のディレクトリパスをシステムパスに追加 sys.path += [blend_dirpath] # 追加ライブラリのインポート import Script_check_surface_bsdf import Script_get_basecolor_bsdf # 指定したオブジェクトのマテリアルを類似マテリアルにマージする def material_marge_object(arg_object:bpy.types.Object) -> bool: """指定したオブジェクトのマテリアルを類似マテリアルにマージする Args: arg_object (bpy.types.Object): 指定オブジェクト Returns: bool: 実行正否 """ # オブジェクトがメッシュであるか確認する if arg_object.type != 'MESH': # 指定オブジェクトがメッシュでない場合は処理しない return None # オブジェクトのマテリアルスロットを走査する for check_material_slot in arg_object.material_slots: # スロットのマテリアルを取得 check_mat = check_material_slot.material # 全マテリアルデータを走査する for comp_material_slot in arg_object.material_slots: # スロットのマテリアルを取得 comp_mat = comp_material_slot.material # 一致マテリアルを検出する前に自身がリストに出た場合 # マージ処理を行わない if check_mat.name == comp_mat.name: break # マテリアルの比較処理 comp_result = comp_material_color(check_mat, comp_mat) # ディフューズ色を比較 if comp_result: # マテリアルを一致したものに差し替え check_material_slot.material = comp_mat # マージ処理を終了する break return True # 指定マテリアルのカラー情報を比較する def comp_material_color(arg_material_one:bpy.types.Material, arg_material_two:bpy.types.Material) -> bool: """指定マテリアルのカラー情報を比較する 受け渡したマテリアルの出力ノードに接続されたプリシプルBSDFノードのベースカラーを比較する 比較したベースカラーが同一の場合、Trueを返す Args: arg_material_one (bpy.types.Material): 比較マテリアル1 arg_material_two (bpy.types.Material): 比較マテリアル2 Returns: bool: 比較結果(一致:True) """ # 比較結果フラグ comp_result = False # カラー情報の変数を用意する material_nodesocketcolor_one = None material_nodesocketcolor_two = None # マテリアルの出力ノードにプリンシプルBSDFノードが接続されているかチェックする if Script_check_surface_bsdf.check_surface_bsdf(arg_material_one): # プリンシプルBSDFノードのベースカラーの値を取得する material_nodesocketcolor_one = Script_get_basecolor_bsdf.get_basecolor_bsdf(arg_material_one) else: # プリシプルBSDF出なかった場合は処理を終了して False を返す return False # マテリアルの出力ノードにプリンシプルBSDFノードが接続されているかチェックする if Script_check_surface_bsdf.check_surface_bsdf(arg_material_two): # プリンシプルBSDFノードのベースカラーの値を取得する material_nodesocketcolor_two = Script_get_basecolor_bsdf.get_basecolor_bsdf(arg_material_two) else: # プリシプルBSDF出なかった場合、処理を終了して False を返す return False # ベースカラーの値が取得できたかチェックする if ((material_nodesocketcolor_one == None) or (material_nodesocketcolor_two == None)): # 値が取得できなかった場合は処理を終了して False を返す return False # カラーソケットの各値が一致するかチェックする if ((material_nodesocketcolor_one[0] == material_nodesocketcolor_two[0]) and (material_nodesocketcolor_one[1] == material_nodesocketcolor_two[1]) and (material_nodesocketcolor_one[2] == material_nodesocketcolor_two[2]) and (material_nodesocketcolor_one[3] == material_nodesocketcolor_two[3])): # 全ての値が一致する場合は一致と見なす comp_result = True return comp_result # 関数の実行例 target_obj = bpy.data.objects.get("Cube") material_marge_object(target_obj)
・Script_check_surface_bsdf.py
# bpyインポート import bpy # 指定マテリアルのアクティブな出力ノードに接続されたノードがプリンシプルBSDFかチェックする def check_surface_bsdf(arg_material:bpy.types.Material) -> bool: """指定マテリアルのアクティブな出力ノードに接続されたノードがプリンシプルBSDFかチェックする Args: arg_material (bpy.types.Material): 指定マテリアル Returns: bool: プリンシプルBSDFが接続されているか """ # マテリアルのノードを有効化する use_material_node(arg_material=arg_material) # アクティブな出力ノードに接続されたノードを取得する get_node = get_node_linkoutput(arg_material=arg_material) # ノードが取得できたか確認する if get_node == None: # サーフェスノードが存在しない場合はFalseを返す return False # ノードの種類がプリンシプルBSDFかチェックして結果を返す isBSDF = check_isnode_bsdf(get_node) return isBSDF # アクティブな出力ノードに接続されたノードを取得する def get_node_linkoutput(arg_material:bpy.types.Material) -> bpy.types.Node: """アクティブな出力ノードに接続されたノードを取得する Args: arg_material (bpy.types.Material): 指定マテリアル Returns: bpy.types.Node: アクティブな出力ノードに接続されたノード """ # 参照の保存用変数 name_mapping = {} # ノード操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Node.html) # ノードリスト操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Nodes.html) # ノードツリー操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeTree.html) # ターゲットマテリアルのノード参照を取得する mat_nodes = arg_material.node_tree.nodes # 出力ノードを取得する変数 output_node = None # 出力ノードの操作マニュアル # (https://docs.blender.org/api/current/bpy.types.ShaderNodeOutputMaterial.html) # 全ノードを走査する for check_node in mat_nodes: # ノードタイプを取得する node_idname = check_node.bl_idname # ノードタイプが出力ノードか確認する if node_idname == 'ShaderNodeOutputMaterial': # アクティブな出力ノードのフラグを取得する is_activeoutput = check_node.is_active_output # アクティブな出力ノードかチェックする if is_activeoutput == True: # アクティブな出力ノードなら保持する output_node = check_node # 出力ノードが取得できたか確認する if output_node == None: # 出力ノードが存在しない場合は処理しない return None # ノードソケット操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeSocket.html) # 出力ノードのサーフェス入力(1番目の入力)のリンクを確認する surface_input = output_node.inputs[0] # リンクが接続されているか確認する if surface_input.is_linked == False: # 出力ノードにサーフェスノードが接続されていない場合は処理しない return None # リンク操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeLink.html#bpy.types.NodeLink) # リンクの一覧を取得する mat_links = arg_material.node_tree.links # 接続元ノードを取得する変数 surface_node = None # リンクを走査する for check_link in mat_links: # 接続先が出力ノードのサーフェス入力か確認する if check_link.to_socket == surface_input: # リンクの接続元ノードを取得する surface_node = check_link.from_node # 接続元ノードが取得できたか確認する if surface_node == None: # 接続元ノードが存在しない場合は処理しない return None # 接続元となっているサーフェスノードを返却する return_node = surface_node return return_node # 対象マテリアルのノードを有効化する def use_material_node(arg_material:bpy.types.Material): """対象マテリアルのノードを有効化する Args: arg_material (bpy.types.Material): 対象マテリアル """ # マテリアル操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Material.html) # ノードが無効な場合、有効化する if arg_material.use_nodes == False: arg_material.use_nodes = True return # 指定ノードがプリンシプルBSDFかチェックする def check_isnode_bsdf(arg_node:bpy.types.Node) -> bool: """指定ノードがプリンシプルBSDFかチェックする Args: arg_node (bpy.types.Node): 指定ノード Returns: bool: プリンシプルBSDFか否か """ # チェック結果 isBSDF = False # ノードタイプを取得する node_idname = arg_node.bl_idname # ノードタイプがプリンシプルBSDFノードか確認する if node_idname == 'ShaderNodeBsdfPrincipled': # プリンシプルBSDFならTrueを返す isBSDF = True return isBSDF
・Script_get_basecolor_bsdf.py
# bpyインポート import bpy # 指定マテリアルのアクティブな出力ノードに接続されたプリンシプルBSDFのベースカラーを取得する def get_basecolor_bsdf(arg_material:bpy.types.Material) -> bpy.types.NodeSocketColor: """指定マテリアルのアクティブな出力ノードに接続されたプリンシプルBSDFのベースカラーを取得する Args: arg_material (bpy.types.Material): 指定マテリアル Returns: bpy.types.NodeSocketColor: ベースカラー(取得失敗時 None) """ # カラーの取得変数を初期化する getBaseColor = None # マテリアルのノードを有効化する use_material_node(arg_material=arg_material) # アクティブな出力ノードに接続されたノードを取得する get_node = get_node_linkoutput(arg_material=arg_material) # ノードが取得できたか確認する if get_node == None: # サーフェスノードが存在しない場合はFalseを返す return False # ノードの種類がプリンシプルBSDFかチェックして結果を返す isBSDF = check_isnode_bsdf(get_node) # プリンスプルBSDFか確認する if isBSDF == True: # プリンシプルBSDFならベースカラーにリンクが貼られているかチェックする isCheckedLink = check_link_bsdf_basecolor(get_node) # リンクが貼られていたか確認する if isCheckedLink == False: # リンクが貼られていないならベースカラー値を取得する getBaseColor = get_value_bsdf_basecolor(get_node) return getBaseColor # アクティブな出力ノードに接続されたノードを取得する def get_node_linkoutput(arg_material:bpy.types.Material) -> bpy.types.Node: """アクティブな出力ノードに接続されたノードを取得する Args: arg_material (bpy.types.Material): 指定マテリアル Returns: bpy.types.Node: アクティブな出力ノードに接続されたノード """ # 参照の保存用変数 name_mapping = {} # ノード操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Node.html) # ノードリスト操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Nodes.html) # ノードツリー操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeTree.html) # ターゲットマテリアルのノード参照を取得する mat_nodes = arg_material.node_tree.nodes # 出力ノードを取得する変数 output_node = None # 出力ノードの操作マニュアル # (https://docs.blender.org/api/current/bpy.types.ShaderNodeOutputMaterial.html) # 全ノードを走査する for check_node in mat_nodes: # ノードタイプを取得する node_idname = check_node.bl_idname # ノードタイプが出力ノードか確認する if node_idname == 'ShaderNodeOutputMaterial': # アクティブな出力ノードのフラグを取得する is_activeoutput = check_node.is_active_output # アクティブな出力ノードかチェックする if is_activeoutput == True: # アクティブな出力ノードなら保持する output_node = check_node # 出力ノードが取得できたか確認する if output_node == None: # 出力ノードが存在しない場合は処理しない return None # ノードソケット操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeSocket.html) # 出力ノードのサーフェス入力(1番目の入力)のリンクを確認する surface_input = output_node.inputs[0] # リンクが接続されているか確認する if surface_input.is_linked == False: # 出力ノードにサーフェスノードが接続されていない場合は処理しない return None # リンク操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeLink.html#bpy.types.NodeLink) # リンクの一覧を取得する mat_links = arg_material.node_tree.links # 接続元ノードを取得する変数 surface_node = None # リンクを走査する for check_link in mat_links: # 接続先が出力ノードのサーフェス入力か確認する if check_link.to_socket == surface_input: # リンクの接続元ノードを取得する surface_node = check_link.from_node # 接続元ノードが取得できたか確認する if surface_node == None: # 接続元ノードが存在しない場合は処理しない return None # 接続元となっているサーフェスノードを返却する return_node = surface_node return return_node # 対象マテリアルのノードを有効化する def use_material_node(arg_material:bpy.types.Material): """対象マテリアルのノードを有効化する Args: arg_material (bpy.types.Material): 対象マテリアル """ # マテリアル操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.Material.html) # ノードが無効な場合、有効化する if arg_material.use_nodes == False: arg_material.use_nodes = True return # 指定ノードがプリンシプルBSDFかチェックする def check_isnode_bsdf(arg_node:bpy.types.Node) -> bool: """指定ノードがプリンシプルBSDFかチェックする Args: arg_node (bpy.types.Node): 指定ノード Returns: bool: プリンシプルBSDFか否か """ # チェック結果 isBSDF = False # ノードタイプを取得する node_idname = arg_node.bl_idname # ノードタイプがプリンシプルBSDFノードか確認する if node_idname == 'ShaderNodeBsdfPrincipled': # プリンシプルBSDFならTrueを返す isBSDF = True return isBSDF # 指定ノードのプリンシプルBSDFのベースカラーにリンクが設定されているかチェックする def check_link_bsdf_basecolor(arg_node:bpy.types.Node) -> bool: """指定ノードのプリンシプルBSDFのベースカラーにリンクが設定されているかチェックする Args: arg_node (bpy.types.Node): 指定ノード Returns: bool: リンクの有無 """ # チェック結果 isLinked = False # ベースカラーのリンクが接続されているか確認する if arg_node.inputs["Base Color"].is_linked == True: # リンクが設定されていればTrueを返す isLinked = True return isLinked # 指定ノードのプリンシプルBSDFのベースカラー値を取得する def get_value_bsdf_basecolor(arg_node:bpy.types.Node) -> bpy.types.NodeSocketColor: """指定ノードのプリンシプルBSDFのベースカラー値を取得する Args: arg_node (bpy.types.Node): 指定ノード Returns: bpy.types.NodeSocketColor: ベースカラー値 """ # ベースカラーのデフォルトカラーを取得する # ノードソケットカラー操作のマニュアル # (https://docs.blender.org/api/current/bpy.types.NodeSocketColor.html) basecolor_value = arg_node.inputs["Base Color"].default_value return basecolor_value
・実行前
・実行後
参考ページ
以前記事にした以下のスクリプトを参考にしています。
bluebirdofoz.hatenablog.com
bluebirdofoz.hatenablog.com
bluebirdofoz.hatenablog.com