本日は Blender2.79 の技術調査枠です。
Blender2.79 の Python スクリプトで3Dモデルのオブジェクトとマテリアルの結合を自動化します。
サンプルモデル
以下のような3つのオブジェクトとそれぞれに同様の3つのマテリアル設定を持つモデルをサンプルとして用意しました。
利用スクリプト
以下のオブジェクトの結合と、類似マテリアルの結合を行うスクリプトを用意しました。
マテリアルの類似判定は[Blenderレンダラー]における[マテリアル]タブ内の設定のみ考慮します。
・ModelMerge.py
# osインポート(ファイルパス処理のため) import os # sysインポート(引数取得のため) import sys # bpyインポート(3D処理のため) import bpy # mathインポート(角度計算のため) import math # CUI実行時のメイン関数 def main_CUI(arg_filepathlist=['C:\\WORK\\Models\\FBXImport.fbx']): # 必要なアドオンを有効化する(用gltf-Exporter) blender_addon_enable() for filepath in arg_filepathlist: # 処理データの有無を確認 if os.path.exists(filepath) == False: # 処理データがない場合、処理を行わない continue # 処理データのディレクトリパスを取得 target_dirpath =os.path.dirname(filepath) # 処理データのファイル名を取得 target_filename=os.path.splitext(os.path.basename(filepath))[0] # 処理データの拡張子を取得 target_fileextension=os.path.splitext(filepath)[1] print('dir:'+target_dirpath) print('name:'+target_filename) print('ext:'+target_fileextension) # シーンのクリア delete_all_objects() # ファイルの読み込み import_result = import_file( arg_filedir=target_dirpath, arg_filename=target_filename, arg_fileextension=target_fileextension ) # 読み込みの成否を確認 if import_result == False: # 読み込みに失敗した場合、処理を行わない continue # オブジェクト群に対してメッシュ結合/マテリアル結合/位置適用を実行する joinmesh_joinmat_transapply() # GLB形式で3Dデータを書き出し export_result = export_file( arg_filedir=target_dirpath, arg_filename=target_filename, arg_fileextension='.glb' ) # .blendファイルを名前を付けて保存 blendfilepath=target_dirpath+'\\'+target_filename+'_export.blend' bpy.ops.wm.save_as_mainfile(filepath=blendfilepath) print('Export blend:'+blendfilepath) # 処理結果を表示 print('Export Result:'+str(export_result)) return # GUI実行時のメイン関数 def main_GUI(): # オブジェクト群に対してメッシュ結合/マテリアル結合/位置適用を実行する joinmesh_joinmat_transapply() return # テスト用関数 def test(): return # 必要なアドオンを有効化する # ※ scripts/addonsへのアドオンコピーが必要 # 引数 # 戻り値 def blender_addon_enable(): # gltf-Exporter アドオンの有効化 bpy.ops.wm.addon_enable(module='io_scene_gltf2') return # オブジェクト群に対してメッシュ結合/マテリアル結合/位置適用を実行する # 引数 # 戻り値 def joinmesh_joinmat_transapply(): # 結合後のオブジェクト名 joinob_name='JoinObject' # メッシュオブジェクトを結合する join_objects_mesh(joinob_name) # マテリアルを結合する joint_material_object(joinob_name) # オブジェクトの位置を適用する apply_object_transform(joinob_name) return # オブジェクト群に対してメッシュ結合/マテリアル結合/位置クリアを実行する # 引数 # 戻り値 def joinmesh_joinmat_transclear(): # 結合後のオブジェクト名 joinob_name='JoinObject' # メッシュオブジェクトを結合する join_objects_mesh(joinob_name) # マテリアルを結合する joint_material_object(joinob_name) # オブジェクトの位置をクリアする clear_object_transform(joinob_name) return # オブジェクト群に対して位置調整を実行する # 引数 arg_filepath:調整移動量 # 戻り値 def transmove(arg_vector=(0.0,0.0,0.0)): # 位置調整 for ob in bpy.context.scene.objects: if ob.type == 'MESH': move_vector=arg_vector add_object_transform(arg_objectname=ob.name,arg_location=move_vector) return # 全オブジェクトの削除 # 引数 # 戻り値 def delete_all_objects(): # 全シーンオブジェクトを削除する for item in bpy.context.scene.objects: bpy.context.scene.objects.unlink(item) # 全データオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # 全メッシュデータを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 全マテリアルデータを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) return # 3Dデータをインポートする # 引数 arg_filedir:ファイルディレクトリ # arg_filename:ファイル名 # arg_fileextension:ファイル拡張子 # 戻り値 読み込み成否 def import_file( arg_filedir='C:\\WORK\\Models', arg_filename='ImportModel', arg_fileextension='.fbx'): # 読み込み成否フラグ import_result = False # 拡張子を判定する if arg_fileextension == '.fbx': # FBXのインポートを実行する fbx_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Import FBX:' + fbx_filepath) import_file_fbx(fbx_filepath) import_result = True elif arg_fileextension == '.ifc': # IFCのインポートを実行する ifc_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Import IFC:' + ifc_filepath) import_file_ifc(ifc_filepath) import_result = True else: unknown_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Unknown file format:' + unknown_filepath) return import_result # IFCモデルをインポートする # 要IfcOpenShell(http://ifcopenshell.org/ifcblender.html) # 引数 arg_filepath:ファイルパス # 戻り値 def import_file_ifc(arg_filepath='D:\\BlenderDir\\Script\\Import.ifc'): # ifc-Blender アドオンの有効化 bpy.ops.wm.addon_enable(module='io_import_scene_ifc') # IFCインポート # bpy.ops.import_scene.ifc() bpy.ops.import_scene.ifc( filepath=arg_filepath, use_names=True, process_relations=False, blender_booleans=False ) return # FBXモデルをインポートする # 引数 arg_filepath:ファイルパス # 戻り値 def import_file_fbx(arg_filepath='D:\\BlenderDir\\Script\\Import.fbx'): # FBXインポート # bpy.ops.import_scene.fbx() bpy.ops.import_scene.fbx( filepath=arg_filepath, use_manual_orientation=False, global_scale=1.0, bake_space_transform=False, use_custom_normals=True, use_image_search=True, use_alpha_decals=False, decal_offset=0.0, use_anim=True, anim_offset=1.0, use_custom_props=True, use_custom_props_enum_as_string=True, ignore_leaf_bones=False, force_connect_children=False, automatic_bone_orientation=False, primary_bone_axis='Y', secondary_bone_axis='X', use_prepost_rot=True ) return # 3Dデータをエクスポートする # 引数 arg_filedir:ファイルディレクトリ # arg_filename:ファイル名 # arg_fileextension:ファイル拡張子 # 戻り値 書き出し成否 def export_file( arg_filedir='C:\\WORK\\Models', arg_filename='ExportModel', arg_fileextension='.fbx'): # 書き出し成否フラグ export_result = False # 拡張子を判定する if arg_fileextension == '.fbx': fbx_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Export FBX:' + fbx_filepath) export_file_fbx(fbx_filepath) export_result = True elif arg_fileextension == '.glb': glb_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Export GLB:' + glb_filepath) export_file_glb(glb_filepath) export_result = True else: unknown_filepath = arg_filedir+'\\'+arg_filename+arg_fileextension print('Unknown file format:' + unknown_filepath) return export_result # FBXモデルをエクスポートする(アーマチュアとメッシュのみ出力) # 引数 arg_filepath:ファイルパス # 戻り値 def export_file_fbx(arg_filepath='D:\\BlenderDir\\Script\\Export.fbx'): # FBXエクスポート # bpy.ops.export_scene.fbx() # エクスポート設定(デフォルト値) bpy.ops.export_scene.fbx( filepath=arg_filepath, version='BIN7400', use_selection=False, global_scale=1.0, apply_unit_scale=True, apply_scale_options='FBX_SCALE_NONE', bake_space_transform=False, object_types={'ARMATURE', 'MESH'}, use_mesh_modifiers=True, use_mesh_modifiers_render=True, mesh_smooth_type='OFF', use_mesh_edges=False, use_tspace=False, use_custom_props=False, add_leaf_bones=True, primary_bone_axis='Y', secondary_bone_axis='X', use_armature_deform_only=False, armature_nodetype='NULL', bake_anim=True, bake_anim_use_all_bones=True, bake_anim_use_nla_strips=True, bake_anim_use_all_actions=True, bake_anim_force_startend_keying=True, bake_anim_step=1.0, bake_anim_simplify_factor=1.0, path_mode='AUTO', embed_textures=False, batch_mode='OFF', use_batch_own_dir=True, use_metadata=True ) # 出力対象の種別 # 'EMPTY':エンプティ # 'CAMERA':カメラ # 'LAMP':ランプ # 'ARMATURE':アーマチュア # 'MESH':メッシュ # 'OTHER':その他 # パスモードの種類 # 'AUTO':自動 # 'ABSOLUTE':絶対 # 'RELATIVE':相対的 # 'MATCH':マッチ # 'STRIP':ストリップパス # 'COPY':コピー return # GLBモデルをエクスポートする # 要GLTFImporter(https://github.com/ksons/gltf-blender-importer) # 引数 arg_filepath:ファイルパス # 戻り値 def export_file_glb(arg_filepath='D:\\BlenderDir\\Script\\Export.glb'): # GLBエクスポート # bpy.ops.export_scene.glb() # エクスポート設定(デフォルト値) bpy.ops.export_scene.glb( filepath=arg_filepath, export_copyright='', export_embed_buffers=False, export_embed_images=False, export_strip=False, export_indices='UNSIGNED_INT', export_force_indices=False, export_texcoords=True, export_normals=True, export_tangents=True, export_materials=True, export_colors=True, export_cameras=False, export_camera_infinite=False, export_selected=False, export_layers=True, export_extras=False, export_yup=True, export_apply=False, export_animations=True, export_frame_range=True, export_frame_step=1, export_move_keyframes=True, export_force_sampling=False, export_current_frame=True, export_skins=True, export_bake_skins=False, export_morph=True, export_morph_normal=True, export_morph_tangent=True, export_lights=False, export_displacement=False, will_save_settings=False ) return # 指定オブジェクトのトランスフォームを変更する # 引数 arg_objectname:指定オブジェクト名 # arg_location:指定位置値(加算) # arg_rotation:指定回転値(加算) # arg_scale:指定拡大縮小値(乗算) # 戻り値 def add_object_transform(arg_objectname='Default', arg_location=(0,0,0),arg_rotation=(0,0,0),arg_scale=(1,1,1)): # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 位置を変更する selectob.location.x += arg_location[0] selectob.location.y += arg_location[1] selectob.location.z += arg_location[2] # 回転を変更する selectob.rotation_euler.x += 2*math.pi/360*arg_rotation[0] selectob.rotation_euler.y += 2*math.pi/360*arg_rotation[1] selectob.rotation_euler.z += 2*math.pi/360*arg_rotation[2] # 拡大縮小を変更する selectob.scale.x *= arg_scale[0] selectob.scale.y *= arg_scale[1] selectob.scale.z *= arg_scale[2] return # メッシュオブジェクトの結合 # 引数 arg_objectname:結合オブジェクト名 # 戻り値 def join_objects_mesh(arg_objectname=''): # シーン中の全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # オブジェクトがメッシュであるか確認する if ob.type == 'MESH': # メッシュであれば選択状態にする ob.select = True # 対象アクティブオブジェクトに切り替える # メッシュはアクティブオブジェクトに結合される bpy.context.scene.objects.active = ob else: # メッシュでなければ選択状態にしない ob.select = False # オブジェクトの結合を実行する bpy.ops.object.join() # 結合オブジェクト名が設定されているか if len(arg_objectname): # オブジェクト名が設定されていれば名前を変更する bpy.context.scene.objects.active.name = arg_objectname return # 指定オブジェクトのトランスフォームを適用する # 引数 arg_objectname:指定オブジェクト名 # arg_location:位置適用 # arg_rotation:回転適用 # arg_scale:拡大縮小適用 # 戻り値 def apply_object_transform(arg_objectname='Default', arg_location=True,arg_rotation=True,arg_scale=True): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態にする ob.select=False # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトを選択状態にする selectob.select=True # トランスフォームを適用する bpy.ops.object.transform_apply(location=arg_location, rotation=arg_rotation, scale=arg_scale) # 処理を完了したら非選択状態にする selectob.select=False return # 指定オブジェクトのトランスフォームをクリアする # 引数 arg_objectname:指定オブジェクト名 # arg_location:位置クリア # arg_rotation:回転クリア # arg_scale:拡大縮小クリア # 戻り値 def clear_object_transform(arg_objectname='Default', arg_location=True,arg_rotation=True,arg_scale=True): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態にする ob.select=False # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトを選択状態にする selectob.select=True # 位置をクリアする if arg_location: bpy.ops.object.location_clear(clear_delta=False) # 回転をクリアする if arg_rotation: bpy.ops.object.rotation_clear(clear_delta=False) # 拡大縮小をクリアする if arg_scale: bpy.ops.object.scale_clear(clear_delta=False) # 処理を完了したら非選択状態にする selectob.select=False return # 指定したオブジェクトのマテリアルを類似マテリアルで結合する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def joint_material_object(arg_objectname='Default'): # 空のマテリアルスロットを削除する delete_materialslot_none(arg_objectname) # 指定したオブジェクトのマテリアルを類似マテリアルにマージする marge_material_object(arg_objectname) # マテリアルスロットをバブルソートで並び替える sort_materialslot_bubble(arg_objectname) # マテリアルスロットの重複を削除する delate_materialslot_duplicate(arg_objectname) return # 指定したオブジェクトのマテリアルを類似マテリアルにマージする # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def marge_material_object(arg_objectname='Default'): # 指定オブジェクトを取得する selectob = bpy.data.objects[arg_objectname] # 変更オブジェクトをアクティブに変更する bpy.context.scene.objects.active = selectob # オブジェクトがメッシュならマージ処理を実行する if selectob.type == 'MESH': # オブジェクトのマテリアルスロットを走査する for material_slot in selectob.material_slots: # スロットのマテリアルを取得 checkmat = material_slot.material # マテリアルのディフューズ色を取得 check_diffuse_color = checkmat.diffuse_color # 全マテリアルデータを走査する for compmat in bpy.data.materials: print('check:' + checkmat.name) print('comp:' + compmat.name) # 一致マテリアルを検出する前に自身がリストに出た場合 # マージ処理を行わない if checkmat.name == compmat.name: break # マテリアルの一致確認 mat_match=compare_material(checkmat.name, compmat.name) # マテリアルの一致判定時の処理 if mat_match: print('match') # マテリアルを一致したものに差し替え material_slot.material = compmat # マージ処理を終了する break else: print('nonmatch') return # 空のマテリアルスロットを削除する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def delete_materialslot_none(arg_objectname='Default'): # 指定オブジェクトを取得する targetob = bpy.data.objects[arg_objectname] # 空のマテリアルスロットを削除する for num in range(len(targetob.material_slots))[::-1]: # 削除処理を行うので逆順に要素を追う targetmat=targetob.material_slots[num].material # マテリアルが空であるか if targetmat == None: # 空ならば削除する targetob.active_material_index=num bpy.ops.object.material_slot_remove() return # マテリアルスロットをバブルソートで並び替える # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def sort_materialslot_bubble(arg_objectname='Default'): # 指定オブジェクトを取得する targetob = bpy.data.objects[arg_objectname] # 比較フラグ change_flg=True # ソートの余地がなくなるまでループ while change_flg: change_flg=False # マテリアルスロットを走査する for num in range(len(targetob.material_slots)-1): # マテリアル名を取得する checkmatname=targetob.material_slots[num].material.name compmatname=targetob.material_slots[num+1].material.name # マテリアル名を比較する if checkmatname > compmatname: # 位置を入れ替える targetob.active_material_index=num bpy.ops.object.material_slot_move(direction='DOWN') # 比較を継続する change_flg=True return # マテリアルスロットの重複を削除する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def delate_materialslot_duplicate(arg_objectname='Default'): # 指定オブジェクトを取得する targetob = bpy.data.objects[arg_objectname] # 重複しているマテリアルスロットを削除する for num in range(len(targetob.material_slots)-1)[::-1]: # 削除処理を行うので逆順に要素を追う checkmatname=targetob.material_slots[num].material.name compmatname=targetob.material_slots[num+1].material.name # マテリアル名を比較する if checkmatname == compmatname: # マテリアル名が同じならば削除する targetob.active_material_index=num+1 bpy.ops.object.material_slot_remove() return # マテリアルの比較関数 # ※ サーフェスタイプのみ対応 # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material( arg_matname01='Default01', arg_matname02='Default02'): # マテリアル名 print('compare_material:matname01='+arg_matname01+',matname02='+arg_matname02) # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアル保持関数 #bpy.context.active_object.active_material. # タイプがサーフェスでない場合、比較処理は非対応 # 比較は行わず、不一致として扱う if ( checkmat.type != 'SURFACE' or compmat.type != 'SURFACE' ): # 不一致判定 return False # ディフューズ要素の比較 diffuse_match=compare_material_diffuse(checkmat.name, compmat.name) if (not diffuse_match): return False # スペキュラー要素の比較 specular_match=compare_material_specular(checkmat.name, compmat.name) if (not specular_match): return False # シェーディング要素の比較 shading_match=compare_material_shading(checkmat.name, compmat.name) if (not shading_match): return False # 透過要素の比較 transparency_match=compare_material_transparency(checkmat.name, compmat.name) if (not transparency_match): return False # ミラー要素の比較 mirror_match=compare_material_mirror(checkmat.name, compmat.name) if (not mirror_match): return False # SSS要素の比較 subsurfacescattering_match=compare_material_subsurfacescattering(checkmat.name, compmat.name) if (not subsurfacescattering_match): return False # ストランド要素の比較 strand_match=compare_material_strand(checkmat.name, compmat.name) if (not strand_match): return False # オプション要素の比較 option_match=compare_material_option(checkmat.name, compmat.name) if (not option_match): return False # 影要素の比較 shadows_match=compare_material_shadows(checkmat.name, compmat.name) if (not shadows_match): return False # その他要素の比較 other_match=compare_material_other(checkmat.name, compmat.name) if (not other_match): return False # ここまでくれば全て一致 return True # マテリアルの比較関数(ディフューズ要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_diffuse( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのディフューズ要素を比較 # シェーダー種別が同じか否か if (checkmat.diffuse_shader != compmat.diffuse_shader): # 不一致判定 return False # ディフューズの基本要素を比較 # - ディフューズ色 # - 強度 # - カラーランプ if ( checkmat.diffuse_color != compmat.diffuse_color or checkmat.diffuse_intensity != compmat.diffuse_intensity or checkmat.use_diffuse_ramp != compmat.use_diffuse_ramp ): # 不一致判定 return False # シェーダ種別ごとに追加要素を比較 # ランバートの場合 if (checkmat.diffuse_shader == 'LAMBERT'): # ランバートは追加要素なし if (False): return False # オーレンネイヤーの場合 if (checkmat.diffuse_shader == 'OREN_NAYAR'): # 粗さを比較 if (checkmat.roughness != compmat.roughness): return False # トゥーンの場合 if (checkmat.diffuse_shader == 'TOON'): # サイズとスムーズを比較 if ( checkmat.diffuse_toon_size != compmat.diffuse_toon_size or checkmat.diffuse_toon_smooth != compmat.diffuse_toon_smooth ): return False # ミンナートの場合 if (checkmat.diffuse_shader == 'MINNAERT'): # 暗闇を比較 if (checkmat.darkness != compmat.darkness): return False # フレネルの場合 if (checkmat.diffuse_shader == 'FRESNEL'): # フレネルと強さを比較 if ( checkmat.diffuse_fresnel != compmat.diffuse_fresnel or checkmat.diffuse_fresnel_factor != compmat.diffuse_fresnel_factor ): return False # ここまでくれば全て一致 return True # マテリアルの比較関数(スペキュラー要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_specular( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのスペキュラー要素を比較 # シェーダー種別が同じか否か if (checkmat.specular_shader != compmat.specular_shader): # 不一致判定 return False # スペキュラーの基本要素を比較 # - スペキュラー色 # - 強度 # - カラーランプ if ( checkmat.specular_color != compmat.specular_color or checkmat.specular_intensity != compmat.specular_intensity or checkmat.use_specular_ramp != compmat.use_specular_ramp ): # 不一致判定 return False # シェーダ種別ごとに追加要素を比較 # クックトランスの場合 if (checkmat.specular_shader == 'COOKTORR'): # 硬さを比較 if (checkmat.specular_hardness != compmat.specular_hardness): return False # フォンの場合 if (checkmat.specular_shader == 'PHONG'): # 硬さを比較 if (checkmat.specular_hardness != compmat.specular_hardness): return False # ブリンの場合 if (checkmat.specular_shader == 'BLINN'): # 硬さとIORを比較 if ( checkmat.specular_hardness != compmat.specular_hardness or checkmat.specular_ior != compmat.specular_ior ): return False # トゥーンの場合 if (checkmat.specular_shader == 'TOON'): # サイズとスムーズを比較 if ( checkmat.specular_toon_size != compmat.specular_toon_size or checkmat.specular_toon_smooth != compmat.specular_toon_smooth ): return False # 異方性ウォードの場合 if (checkmat.specular_shader == 'WARDISO'): # スロープを比較 if (checkmat.specular_slope != compmat.specular_slope): return False # ここまでくれば全て一致 return True # マテリアルの比較関数(シェーディング要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_shading( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのシェーディング要素を比較 # - 放射 # - 周囲 # - 透光性 # - 陰影なし # - タンジェントシェーディング # - 三次補間 if ( checkmat.emit != compmat.emit or checkmat.ambient != compmat.ambient or checkmat.translucency != compmat.translucency or checkmat.use_shadeless != compmat.use_shadeless or checkmat.use_tangent_shading != compmat.use_tangent_shading or checkmat.use_cubic != compmat.use_cubic ): # 不一致判定 return False # ここまでくれば全て一致 return True # マテリアルの比較関数(透過要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_transparency( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # 透過が共に無効であれば比較は行わない if ( checkmat.use_transparency == False and compmat.use_transparency == False ): # 一致判定 return True # マテリアルの透過要素を比較 # 透過が有効か否か if (checkmat.use_transparency != compmat.use_transparency): # 不一致判定 return False # 透過の種別が同じか否か if (checkmat.transparency_method != compmat.transparency_method): # 不一致判定 return False # 透過の基本要素を比較 # - アルファ # - フレネル # - スペキュラ # - ブレンド if ( checkmat.alpha != compmat.alpha or checkmat.fresnel != compmat.fresnel or checkmat.specular_alpha != compmat.specular_alpha or checkmat.fresnel_factor != compmat.fresnel_factor ): # 不一致判定 return False # 透過種別ごとに追加要素を比較 # マスクの場合 if (checkmat.transparency_method == 'MASK'): # マスクは追加要素なし if (False): return False # Z値透過の場合 if (checkmat.transparency_method == 'Z_TRANSPARENCY'): # Z値透過は追加要素なし if (False): return False # レイトレースの場合 if (checkmat.transparency_method == 'RAYTRACE'): # 追加要素を比較 # - IOR # - フィルタ # - 減衰 # - 制限 # - 深度 # - 量 # - 閾値 # - サンプル checkmat_raytrace=checkmat.raytrace_transparency compmat_raytrace=compmat.raytrace_transparency if ( checkmat_raytrace.ior != compmat_raytrace.ior or checkmat_raytrace.filter != compmat_raytrace.filter or checkmat_raytrace.falloff != compmat_raytrace.falloff or checkmat_raytrace.depth_max != compmat_raytrace.depth_max or checkmat_raytrace.depth != compmat_raytrace.depth or checkmat_raytrace.gloss_factor != compmat_raytrace.gloss_factor or checkmat_raytrace.gloss_threshold != compmat_raytrace.gloss_threshold or checkmat_raytrace.gloss_samples != compmat_raytrace.gloss_samples ): return False # ここまでくれば全て一致 return True # マテリアルの比較関数(ミラー要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_mirror( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # ミラーが共に無効であれば比較は行わない if ( checkmat.raytrace_mirror.use == False and compmat.raytrace_mirror.use == False ): # 一致判定 return True # マテリアルのミラー要素を比較 # ミラーが有効か否か if (checkmat.raytrace_mirror.use != compmat.raytrace_mirror.use): # 不一致判定 return False # ミラーの要素を比較 # - 反射率 # - フレネル # - カラー # - ブレンド # - 深度 # - 最大距離 # - 背景 # - 量 # - 閾値 # - サンプル # - 異方性 if ( checkmat.raytrace_mirror.reflect_factor != compmat.alpharaytrace_mirror.reflect_factor or checkmat.raytrace_mirror.fresnel != compmat.alpharaytrace_mirror.fresnel or checkmat.raytrace_mirror.fresnel_factor != compmat.alpharaytrace_mirror.fresnel_factor or checkmat.mirro_color != compmat.mirro_color or checkmat.raytrace_mirror.depth != compmat.raytrace_mirror.depth or checkmat.raytrace_mirror.distance != compmat.raytrace_mirror.distance or checkmat.raytrace_mirror.fade_to != compmat.raytrace_mirror.fade_to or checkmat.raytrace_mirror.gloss_factor != compmat.raytrace_mirror.gloss_factor or checkmat.raytrace_mirror.gloss_threshold != compmat.raytrace_mirror.gloss_threshold or checkmat.raytrace_mirror.gloss_samples != compmat.raytrace_mirror.gloss_samples or checkmat.raytrace_mirror.gloss_anisotropic != compmat.raytrace_mirror.gloss_anisotropic ): # 不一致判定 return False # ここまでくれば全て一致 return True # マテリアルの比較関数(SSS要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_subsurfacescattering( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # SSSが共に無効であれば比較は行わない if ( checkmat.subsurface_scattering.use == False and compmat.subsurface_scattering.use == False ): # 一致判定 return True # マテリアルのSSS要素を比較 # SSSが有効か否か if (checkmat.subsurface_scattering.use != compmat.subsurface_scattering.use): # 不一致判定 return False # SSSの要素を比較 # - IOR # - 拡大縮小 # - カラー # - RGB半径 # - ブレンドカラー # - ブレンドテクスチャ # - 散乱ウェイト(前) # - 散乱ウェイト(後) # - エラー if ( checkmat.subsurface_scattering.ior != compmat.subsurface_scattering.ior or checkmat.subsurface_scattering.scale != compmat.subsurface_scattering.scale or checkmat.subsurface_scattering.color != compmat.subsurface_scattering.color or checkmat.subsurface_scattering.radius[0] != compmat.subsurface_scattering.radius[0] or checkmat.subsurface_scattering.radius[1] != compmat.subsurface_scattering.radius[1] or checkmat.subsurface_scattering.radius[2] != compmat.subsurface_scattering.radius[2] or checkmat.subsurface_scattering.color_factor != compmat.subsurface_scattering.color_factor or checkmat.subsurface_scattering.texture_factor != compmat.subsurface_scattering.texture_factor or checkmat.subsurface_scattering.front != compmat.subsurface_scattering.front or checkmat.subsurface_scattering.back != compmat.subsurface_scattering.back or checkmat.subsurface_scattering.error_threshold != compmat.subsurface_scattering.error_threshold ): # 不一致判定 return False return True # マテリアルの比較関数(ストランド要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_strand( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのストランド要素を比較 # - サイズルート # - サイズ先端 # - サイズ最小 # - シェーディング幅を減衰 # - シェーディングレイヤー参照 # - ブレンダー単位 # - タンジェントシェーディング # - シェイプ # - 距離 if ( checkmat.strand.root_size != compmat.strand.root_size or checkmat.strand.tip_size != compmat.strand.tip_size or checkmat.strand.size_min != compmat.strand.size_min or checkmat.strand.width_fade != compmat.strand.width_fade or checkmat.strand.uv_layer != compmat.strand.uv_layer or checkmat.strand.use_blender_units != compmat.strand.use_blender_units or checkmat.strand.use_tangent_shading != compmat.strand.use_tangent_shading or checkmat.strand.shape != compmat.strand.shape or checkmat.strand.blend_distance != compmat.strand.blend_distance ): # 不一致判定 return False # ここまでくれば全て一致 return True # マテリアルの比較関数(オプション要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_option( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのオプション要素を比較 # - レイトレース # - フルオーバーサンプリング # - 背景 # - ミスとを使用 # - Z深度を反転 # - Zオフセット # - 照明グループ参照 # - 照明グループ排他 # - 照明グループローカル # - 面テクスチャ # - 面テクスチャのアルファ # - 頂点カラーペイント # - 頂点カラーライト # - オブジェクトカラー # - UV投影 # - パスインデックス if ( checkmat.use_raytrace != compmat.use_raytrace or checkmat.use_full_oversampling != compmat.use_full_oversampling or checkmat.use_sky != compmat.use_sky or checkmat.use_mist != compmat.use_mist or checkmat.invert_z != compmat.invert_z or checkmat.offset_z != compmat.offset_z or checkmat.light_group != compmat.light_group or checkmat.use_light_group_exclusive != compmat.use_light_group_exclusive or checkmat.use_light_group_local != compmat.use_light_group_local or checkmat.use_face_texture != compmat.use_face_texture or checkmat.use_face_texture_alpha != compmat.use_face_texture_alpha or checkmat.use_vertex_color_paint != compmat.use_vertex_color_paint or checkmat.use_vertex_color_light != compmat.use_vertex_color_light or checkmat.use_object_color != compmat.use_object_color or checkmat.use_uv_project != compmat.use_uv_project or checkmat.pass_index != compmat.pass_index ): # 不一致判定 return False # ここまでくれば全て一致 return True # マテリアルの比較関数(影要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_shadows( arg_matname01='Default01', arg_matname02='Default02'): # マテリアル名 print('matname01='+arg_matname01+',matname02='+arg_matname02) # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルの影要素を比較 # - 影を受信 # - 半透明影の受信 # - 受けた影のみ # - 影のタイプ # - オートレイバイアス # - レイバイアス # - 成型 # - 投影のみ # - バッファーシャドウを投影 # - 半透明影 # - バッファーバイアス # - 近似AOを投影 if ( checkmat.use_shadows != compmat.use_shadows or checkmat.use_transparent_shadows != compmat.use_transparent_shadows or checkmat.use_only_shadow != compmat.use_only_shadow or checkmat.shadow_only_type != compmat.shadow_only_type or checkmat.use_ray_shadow_bias != compmat.use_ray_shadow_bias or checkmat.shadow_ray_bias != compmat.shadow_ray_bias or checkmat.use_cast_shadows != compmat.use_cast_shadows or checkmat.use_cast_shadows_only != compmat.use_cast_shadows_only or checkmat.use_cast_buffer_shadows != compmat.use_cast_buffer_shadows or checkmat.shadow_cast_alpha != compmat.shadow_cast_alpha or checkmat.shadow_buffer_bias != compmat.shadow_buffer_bias or checkmat.use_cast_approximate != compmat.use_cast_approximate ): # 不一致判定 return False # ここまでくれば全て一致 return True # マテリアルの比較関数(その他要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_material_other( arg_matname01='Default01', arg_matname02='Default02'): # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # マテリアルのその他要素を比較 # - ノード利用 # - マテリアルタイプ if ( checkmat.use_nodes != compmat.use_nodes or checkmat.type != compmat.type ): # 不一致判定 return False # ここまでくれば全て一致 return True # テクスチャスロットの比較関数 # ※ サーフェスタイプのみ対応 # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # 戻り値 一致/不一致(True/False) def compare_materialslots( arg_matname01='Default01', arg_matname02='Default02'): # マテリアル名 print('compare_materialslots:matname01='+arg_matname01+',matname02='+arg_matname02) # 比較マテリアルを取得 checkmat=bpy.data.materials[arg_matname01] compmat=bpy.data.materials[arg_matname02] # テクスチャスロット数の比較 checkslotnum=len(checkmat.texture_slots) compslotnum=len(compmat.texture_slots) if (checkslotnum != compslotnum): # 不一致判定 return False # 各テクスチャスロット要素の比較 for num in range(checkslotnum): # マッピング要素の比較 mapping_match=compare_textureslot_mapping(checkmat.name, compmat.name, num) if (not mapping_match): return False # 影響要素の比較 impact_match=compare_textureslot_impact(checkmat.name, compmat.name, num) if (not impact_match): return False # その他要素の比較 other_match=compare_textureslot_other(checkmat.name, compmat.name, num) if (not other_match): return False # テクスチャの取得 checktex=checkmat.texture_slots.texture comptex=compmat.texture_slots.texture # テクスチャの取得確認 if (checktex == None or comptex == None): if (checktex == None and comptex == None): # 共に画像なし continue else: # 一方のみ画像なし return False # テクスチャ要素の比較 texture_match=compare_texture(checktex.name, comptex.name) if (not texture_match): return False # ここまでくれば全て一致 return True # テクスチャスロットの比較関数(マッピング要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # arg_slotnumber:スロット番号 # 戻り値 一致/不一致(True/False) def compare_textureslot_mapping( arg_matname01='Default01', arg_matname02='Default02', arg_slotnumber=0): # 比較テクスチャスロットを取得 checktexslot=bpy.data.materials[arg_matname01].texture_slots[arg_slotnumber] comptexslot=bpy.data.materials[arg_matname02].texture_slots[arg_slotnumber] # マッピング要素を比較 # - 投影 # - 投影のマッピング # - オフセット # - サイズ if ( checktexslot.mapping != comptexslot.mapping or checktexslot.mapping_x != comptexslot.mapping_x or checktexslot.mapping_y != comptexslot.mapping_y or checktexslot.mapping_z != comptexslot.mapping_z or checktexslot.offset[0] != comptexslot.offset[0] or checktexslot.offset[1] != comptexslot.offset[1] or checktexslot.offset[2] != comptexslot.offset[2] or checktexslot.scale[0] != comptexslot.scale[0] or checktexslot.scale[1] != comptexslot.scale[1] or checktexslot.scale[2] != comptexslot.scale[2] ): # 不一致判定 return False # 座標の種別が同じか否か if (checktexslot.texture_coords != comptexslot.texture_coords): # 不一致判定 return False # 座標の種別ごとに追加要素を比較 # グローバルの場合 if (checktexslot.texture_coords == 'GLOBAL'): # グローバルは追加要素なし if (False): return False # オブジェクトの場合 if (checktexslot.texture_coords == 'OBJECT'): # 追加要素を比較 # - オブジェクト # - オリジナルから if ( checktexslot.object != checktexslot.object or checktexslot.use_from_original != checktexslot.use_from_original ): return False # UVの場合 if (checktexslot.texture_coords == 'UV'): # 追加要素を比較 # - マップ # - 複製の親 if ( checktexslot.map != checktexslot.map or checktexslot.use_from_dupli != checktexslot.use_from_dupli ): return False # 生成の場合 if (checktexslot.texture_coords == 'ORCO'): # 追加要素を比較 # - 複製の親 if (checktexslot.use_from_dupli != checktexslot.use_from_dupli): return False # ストランドパーティクルの場合 if (checktexslot.texture_coords == 'STRAND'): # ストランドパーティクルは追加要素なし if (False): return False # ウィンドウの場合 if (checktexslot.texture_coords == 'WINDOW'): # ウィンドウは追加要素なし if (False): return False # ノーマルの場合 if (checktexslot.texture_coords == 'NORMAL'): # ノーマルは追加要素なし if (False): return False # 反射の場合 if (checktexslot.texture_coords == 'REFLECTION'): # 反射は追加要素なし if (False): return False # ストレスの場合 if (checktexslot.texture_coords == 'STRESS'): # ストレスは追加要素なし if (False): return False # タンジェントの場合 if (checktexslot.texture_coords == 'TANGENT'): # タンジェントは追加要素なし if (False): return False # ここまでくれば全て一致 return True # テクスチャスロットの比較関数(影響要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # arg_slotnumber:スロット番号 # 戻り値 一致/不一致(True/False) def compare_textureslot_impact( arg_matname01='Default01', arg_matname02='Default02', arg_slotnumber=0): # 比較テクスチャスロットを取得 checktexslot=bpy.data.materials[arg_matname01].texture_slots[arg_slotnumber] comptexslot=bpy.data.materials[arg_matname02].texture_slots[arg_slotnumber] # ディフューズ要素を比較 # - ディフューズ強度 # - ディフューズカラー # - ディフューズアルファ # - ディフューズ透光性 if ( checktexslot.use_map_diffuse != comptexslot.use_map_diffuse or checktexslot.diffuse_factor != comptexslot.diffuse_factor or checktexslot.use_map_color_diffuse != comptexslot.use_map_color_diffuse or checktexslot.diffuse_color_factor != comptexslot.diffuse_color_factor or checktexslot.use_map_alpha != comptexslot.use_map_alpha or checktexslot.alpha_factor != comptexslot.alpha_factor or checktexslot.use_map_translucency != comptexslot.use_map_translucency or checktexslot.translucency_factor != comptexslot.translucency_factor ): # 不一致判定 return False # シェーディング要素を比較 # - シェーディング周囲 # - シェーディング放射 # - シェーディングミラー # - シェーディングレイミラー if ( checktexslot.use_map_ambient != comptexslot.use_map_ambient or checktexslot.ambient_factor != comptexslot.ambient_factor or checktexslot.use_map_emit != comptexslot.use_map_emit or checktexslot.emit_factor != comptexslot.emit_factor or checktexslot.use_map_mirror != comptexslot.use_map_mirror or checktexslot.mirror_factor != comptexslot.mirror_factor or checktexslot.use_map_raymir != comptexslot.use_map_raymir or checktexslot.raymir_factor != comptexslot.raymir_factor ): # 不一致判定 return False # スペキュラー要素を比較 # - スペキュラー強度 # - スペキュラーカラー # - スペキュラー硬さ if ( checktexslot.use_map_specular != comptexslot.use_map_specular or checktexslot.specular_factor != comptexslot.specular_factor or checktexslot.use_map_color_spec != comptexslot.use_map_color_spec or checktexslot.specular_color_factor != comptexslot.specular_color_factor or checktexslot.use_map_hardness != comptexslot.use_map_hardness or checktexslot.hardness_factor != comptexslot.hardness_factor ): # 不一致判定 return False # ジオメトリ要素を比較 # - ジオメトリノーマル # - ジオメトリワープ # - ジオメトリディスプレイス if ( checktexslot.use_map_normal != comptexslot.use_map_normal or checktexslot.normal_factor != comptexslot.normal_factor or checktexslot.use_map_warp != comptexslot.use_map_warp or checktexslot.warp_factor != comptexslot.warp_factor or checktexslot.use_map_displacement != comptexslot.use_map_displacement or checktexslot.displacement_factor != comptexslot.displacement_factor ): # 不一致判定 return False # ブレンド要素を比較 # - ブレンド # - RGBを強度に変換 # - カラー # - 負 # - マスク # - デフォルト値 if ( checktexslot.blend_type != comptexslot.blend_type or checktexslot.use_rgb_to_intensity != comptexslot.use_rgb_to_intensity or checktexslot.color != comptexslot.color or checktexslot.invert != comptexslot.invert or checktexslot.use_stencil != comptexslot.use_stencil or checktexslot.default_value != comptexslot.default_value ): # 不一致判定 return False # バンプマッピング要素を比較 # - 方式 # - スペース if ( checktexslot.bump_method != comptexslot.bump_method or checktexslot.bump_objectspace != comptexslot.bump_objectspace ): # 不一致判定 return False # ここまでくれば全て一致 return True # テクスチャスロットの比較関数(その他要素) # 引数 arg_matname01:比較マテリアル名1 # arg_matname02:比較マテリアル名2 # arg_slotnumber:スロット番号 # 戻り値 一致/不一致(True/False) def compare_textureslot_other( arg_matname01='Default01', arg_matname02='Default02', arg_slotnumber=0): # 比較テクスチャスロットを取得 checktexslot=bpy.data.materials[arg_matname01].texture_slots[arg_slotnumber] comptexslot=bpy.data.materials[arg_matname02].texture_slots[arg_slotnumber] # 画像のサンプリング要素を比較 # - ノーマルマップ空間 if (checktexslot.normal_map_space != comptexslot.normal_map_space): # 不一致判定 return False # ここまでくれば全て一致 return True # テクスチャの比較関数 # ※ 画像または動画タイプのみ対応 # 引数 arg_texname01:比較テクスチャ名1 # arg_texname02:比較テクスチャ名2 # 戻り値 一致/不一致(True/False) def compare_texture( arg_texname01='Default01', arg_texname02='Default02'): # テクスチャ名 print('compare_texture:texname01='+arg_texname01+',texname02='+arg_texname02) # 比較テクスチャを取得 checktex=bpy.data.textures[arg_texname01] comptex=bpy.data.textures[arg_texname02] # マテリアル保持関数 #bpy.context.active_object.active_texture. # タイプが[画像または動画]でない場合、比較処理は非対応 # 比較は行わず、不一致として扱う if ( checktex.type != 'IMAGE' or comptex.type != 'IMAGE' ): # 不一致判定 return False # 色要素の比較 color_match=compare_texture_color(checktex.name, comptex.name) if (not color_match): return False # 画像要素の比較(単一画像タイプのみ対応) image_match=compare_texture_image(checktex.name, comptex.name) if (not image_match): return False # 画像のサンプリング要素の比較 sampling_match=compare_texture_sampling(checktex.name, comptex.name) if (not sampling_match): return False # 画像のマッピング要素の比較 mapping_match=compare_texture_mapping(checktex.name, comptex.name) if (not mapping_match): return False # ここまでくれば全て一致 return True # テクスチャの比較関数(色要素) # 引数 arg_texname01:比較テクスチャ名1 # arg_texname02:比較テクスチャ名2 # 戻り値 一致/不一致(True/False) def compare_texture_color( arg_texname01='Default01', arg_texname02='Default02'): # 比較テクスチャを取得 checktex=bpy.data.textures[arg_texname01] comptex=bpy.data.textures[arg_texname02] # テクスチャの色要素を比較 # - カラーランプ # - RGBの乗数 # - 明るさ # - コントラスト # - 彩度 # - 範囲制限 if ( checktex.use_color_ramp != comptex.use_color_ramp or checktex.factor_red != comptex.factor_red or checktex.factor_green != comptex.factor_green or checktex.factor_blue != comptex.factor_blue or checktex.intensity != comptex.intensity or checktex.contrast != comptex.contrast or checktex.saturation != comptex.saturation or checktex.use_clamp != comptex.use_clamp ): # 不一致判定 return False # ここまでくれば全て一致 return True # テクスチャの比較関数(画像要素) # ※ 単一画像タイプのみ対応 # 引数 arg_texname01:比較テクスチャ名1 # arg_texname02:比較テクスチャ名2 # 戻り値 一致/不一致(True/False) def compare_texture_image( arg_texname01='Default01', arg_texname02='Default02'): # 比較テクスチャ画像を取得 checkimage=bpy.data.textures[arg_texname01].image compimage=bpy.data.textures[arg_texname02].image # 画像の取得確認 if (checkimage == None or compimage == None): if (checkimage == None and compimage == None): # 共に画像なし return True else: # 一方のみ画像なし return False # タイプが[画像または動画]でない場合、比較処理は非対応 # 比較は行わず、不一致として扱う if ( checkimage.source != 'FILE' or checkimage.source != 'FILE' ): # 不一致判定 return False # テクスチャの画像要素を比較 # - 名前 # - ソース # - 色空間 # - ビューにも適用 # - アルファを使用 # - アルファ # - フィールド # - フィールド優先 if ( checkimage.name != compimage.name or checkimage.filepath != compimage.filepath or checkimage.colorspace_settings.name != compimage.colorspace_settings.name or checkimage.use_view_as_render != compimage.use_view_as_render or checkimage.use_alpha != compimage.use_alpha or checkimage.alpha_mode != compimage.alpha_mode or checkimage.use_fields != compimage.use_fields or checkimage.field_order != compimage.field_order ): # 不一致判定 return False # ここまでくれば全て一致 return True # テクスチャの比較関数(画像のサンプリング要素) # 引数 arg_texname01:比較テクスチャ名1 # arg_texname02:比較テクスチャ名2 # 戻り値 一致/不一致(True/False) def compare_texture_sampling( arg_texname01='Default01', arg_texname02='Default02'): # 比較テクスチャを取得 checktex=bpy.data.textures[arg_texname01] comptex=bpy.data.textures[arg_texname02] # 画像のサンプリング要素を比較 # - アルファ使用 # - アルファ計算 # - アルファ反転 # - X/Y軸を反転 # - ノーマルマップ # - 微分マップ # - ミップマップ # - ミップマップガウシアンフィルタ # - 補間 if ( checktex.use_alpha != comptex.use_alpha or checktex.use_calculate_alpha != comptex.use_calculate_alpha or checktex.invert_alpha != comptex.invert_alpha or checktex.use_flip_axis != comptex.use_flip_axis or checktex.use_normal_map != comptex.use_normal_map or checktex.use_derivative_map != comptex.use_derivative_map or checktex.use_mipmap != comptex.use_mipmap or checktex.use_mipmap_gauss != comptex.use_mipmap_gauss or checktex.use_interpolation != comptex.use_interpolation ): # 不一致判定 return False # フィルタの種別が同じか否か if (checktex.filter_type != comptex.filter_type): # 不一致判定 return False # フィルタ種別ごとに追加要素を比較 # ボックスの場合 if (checktex.filter_type == 'BOX'): # 追加要素を比較 # - フィルターサイズ # - 最小フィルターサイズ if ( checktex.filter_size != checktex.filter_size or checktex.use_filter_size_min != checktex.use_filter_size_min ): return False # EWAの場合 if (checktex.filter_type == 'EWA'): # 追加要素を比較 # - 偏心率 # - フィルターサイズ # - 最小フィルターサイズ if ( checktex.filter_eccentricity != checktex.filter_eccentricity or checktex.filter_size != checktex.filter_size or checktex.use_filter_size_min != checktex.use_filter_size_min ): return False # FELINEの場合 if (checktex.filter_type == 'FELINE'): # 追加要素を比較 # - ブローブ # - フィルターサイズ # - 最小フィルターサイズ if ( checktex.filter_probes != checktex.filter_probes or checktex.filter_size != checktex.filter_size or checktex.use_filter_size_min != checktex.use_filter_size_min ): return False # エリアの場合 if (checktex.filter_type == 'AREA'): # 追加要素を比較 # - 偏心率 # - フィルターサイズ # - 最小フィルターサイズ if ( checktex.filter_eccentricity != checktex.filter_eccentricity or checktex.filter_size != checktex.filter_size or checktex.use_filter_size_min != checktex.use_filter_size_min ): return False # ここまでくれば全て一致 return True # テクスチャの比較関数(画像のマッピング要素) # 引数 arg_texname01:比較テクスチャ名1 # arg_texname02:比較テクスチャ名2 # 戻り値 一致/不一致(True/False) def compare_texture_mapping( arg_texname01='Default01', arg_texname02='Default02'): # 比較テクスチャを取得 checktex=bpy.data.textures[arg_texname01] comptex=bpy.data.textures[arg_texname02] # 画像のマッピング要素を比較 # - 切り抜き最小 # - 切り抜き最大 if ( checktex.crop_min_x != comptex.crop_min_x or checktex.crop_min_y != comptex.crop_min_y or checktex.crop_max_x != comptex.crop_max_x or checktex.crop_max_y != comptex.crop_max_y ): # 不一致判定 return False # 延長の種別が同じか否か if (checktex.extension != comptex.extension): # 不一致判定 return False # フィルタ種別ごとに追加要素を比較 # チェッカーの場合 if (checktex.filter_type == 'CHECKER'): # 追加要素を比較 # - 均一 # - 奇数 # - 距離 if ( checktex.use_checker_even != checktex.use_checker_even or checktex.use_checker_odd != checktex.use_checker_odd or checktex.checker_distance != checktex.checker_distance ): return False # リピートの場合 if (checktex.filter_type == 'REPEAT'): # 追加要素を比較 # - リピート # - ミラー if ( checktex.repeat_x != checktex.repeat_x or checktex.repeat_y != checktex.repeat_y or checktex.use_mirror_x != checktex.use_mirror_x or checktex.use_mirror_y != checktex.use_mirror_y ): return False # 正方形クリップの場合 if (checktex.filter_type == 'CLIP_CUBE'): # 正方形クリップは追加要素なし if (False): return False # クリップの場合 if (checktex.filter_type == 'CLIP'): # クリップは追加要素なし if (False): return False # 拡張の場合 if (checktex.filter_type == 'EXTEND'): # 拡張は追加要素なし if (False): return False # ここまでくれば全て一致 return True # バックグラウンド実行時は bpy.app.background が True if bpy.app.background: # 実行指定で呼び出されているかチェック if __name__ == "__main__": # 引数を取得 args = sys.argv if 2 <= len(args): print('--------------------------- START ---------------------------') # 引数から自身のパスのみpop削除 args.pop(0) print(args) # main関数の呼び出し # バックグラウンド実行時処理 main_CUI(args) print('--------------------------- END ---------------------------') else: # GUI実行時 main_GUI()
実行結果
プロジェクトにスクリプトを読み込み、[スクリプト実行]を行います。
処理が完了すると、以下のようにオブジェクトが1つに結合されます。
かつ、類似マテリアルも結合されて必要最低限のマテリアル数になっています。
CUIでの実行
本記事のスクリプトは以下の記事と合わせて利用すると、処理をCUIから実行することも可能です。
bluebirdofoz.hatenablog.com
Blender2.8への移植
本スクリプトはマテリアルの判定が[Blenderレンダラー]に依存しているため、Blender2.8への移植はできません。
Blender2.8用スクリプトにまとめた上で公開予定でしたが、本スクリプトは移植が厳しいため、Blender2.79向けのまま記事にしました。
次はベイクを利用して更にマテリアルを1つに統合する例です。
bluebirdofoz.hatenablog.com