Blenderに限らず制作をしていると テクスチャ画像等の外部参照する素材が増えてきます
複数人で受け渡ししたい場合に 決まった場所以外にあるファイルを参照していて受け渡しトラブルになりがちです
AfterEffectsの「ファイルを収集」のように
決められたフォルダに外部ファイルを一つの場所にまとめるアドオンを作成してみました
y_CollectExternalFiles.py
bl_info = {
"name": "Collect External Files",
"author": "Yukimituki",
"version": (1, 2),
"blender": (3, 00, 0),
"location": "Properties > Scene > Collect External Files",
"description": "Checks external referenced files (images, libraries) and copies them to specified folders with relative paths.",
"category": "Import-Export",
}
import bpy
import os
import shutil
from bpy.types import Operator, Panel
from bpy.props import StringProperty
class EXTCOLLECT_OT_Collect_External_files(Operator):
"""Check and copy external files to specified folders"""
bl_idname = "external_data.check_and_collect_files"
bl_label = "Check and Collect External Files"
bl_options = {'REGISTER', 'UNDO'}
def get_unique_filepath(self, target_path):
# 同名ファイルが存在する場合、連番を付加してユニークなファイルパスを生成
# Generate a unique file path by appending a number if the file already exists
base, ext = os.path.splitext(target_path)
counter = 1
new_path = target_path
while os.path.exists(new_path):
new_path = f"{base}_{counter:03d}{ext}"
counter += 1
return new_path
def execute(self, context):
# 現在の.blendファイルが保存されているか確認
# Check if the current .blend file is saved
if not bpy.data.filepath:
self.report({'ERROR'}, "Please save the .blend file first to use relative paths! Blenderを保存してから実行してください")
return {'CANCELLED'}
scene = context.scene
image_dir = bpy.path.abspath(scene.extcopier_image_dir)
library_dir = bpy.path.abspath(scene.extcopier_library_dir)
# フォルダが指定されていない場合のエラーチェック
# Error check for unspecified directories
if not image_dir or not library_dir:
self.report({'ERROR'}, "Please specify both image and library directories! フォルダを指定してください")
return {'CANCELLED'}
# フォルダが存在しない場合は作成
# Create directories if they don't exist
if not os.path.exists(image_dir):
os.makedirs(image_dir)
if not os.path.exists(library_dir):
os.makedirs(library_dir)
# 外部参照ファイルの収集
# Collect external referenced files
external_files = []
# 画像
# Images
for img in bpy.data.images:
if img.filepath and img.source != 'GENERATED':
abs_path = bpy.path.abspath(img.filepath)
if os.path.exists(abs_path):
external_files.append((img, abs_path, image_dir))
# ライブラリ(リンクされた.blendファイル)
# Libraries (linked .blend files)
for lib in bpy.data.libraries:
abs_path = bpy.path.abspath(lib.filepath)
if os.path.exists(abs_path):
external_files.append((lib, abs_path, library_dir))
copied_files = 0
for data_block, file_path, target_dir in external_files:
file_name = os.path.basename(file_path)
target_path = os.path.join(target_dir, file_name)
# 同名ファイルが存在する場合、連番を付加
# Append a number if the file already exists
final_target_path = self.get_unique_filepath(target_path)
try:
shutil.copy2(file_path, final_target_path)
self.report({'INFO'}, f"Copied: {file_name} to {final_target_path}")
copied_files += 1
# 相対パスに変換してデータブロックのfilepathを更新
# Convert to relative path and update the data block's filepath
relative_path = bpy.path.relpath(final_target_path)
data_block.filepath = relative_path
except Exception as e:
self.report({'ERROR'}, f"Failed to copy {file_name}: {str(e)}")
# 結果をレポート
# Report the results
if copied_files == 0:
self.report({'INFO'}, "No files needed to be copied.")
else:
self.report({'INFO'}, f"Copied {copied_files} files.")
return {'FINISHED'}
class EXTCOPIER_PT_panel(Panel):
"""Panel for checking and copying external files"""
bl_label = "Collect External Files"
bl_idname = "EXTCOPIER_PT_panel"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.label(text="Target Directories:")
layout.prop(scene, "extcopier_image_dir", text="Images")
layout.prop(scene, "extcopier_library_dir", text="Libraries")
layout.operator("external_data.check_and_collect_files", text="Check and Collect Files")
def register():
# プロパティの登録
# Register properties
bpy.types.Scene.extcopier_image_dir = StringProperty(
name="Image Directory",
description="Directory to copy image files",
subtype='DIR_PATH',
default="//Textures/"
)
bpy.types.Scene.extcopier_library_dir = StringProperty(
name="Library Directory",
description="Directory to copy library files",
subtype='DIR_PATH',
default="//Objects/"
)
bpy.utils.register_class(EXTCOLLECT_OT_Collect_External_files)
bpy.utils.register_class(EXTCOPIER_PT_panel)
def unregister():
# プロパティの削除
# Unregister properties
del bpy.types.Scene.extcopier_image_dir
del bpy.types.Scene.extcopier_library_dir
bpy.utils.unregister_class(EXTCOLLECT_OT_Collect_External_files)
bpy.utils.unregister_class(EXTCOPIER_PT_panel)
if __name__ == "__main__":
register()
プロパティエディタのシーンタブに追加するアドオンです
.blendファイルのある場所に このパネルで設定したフォルダを作成して
外部参照している画像や リンクしている.blendファイルをコピーします
ファイルパスの設定は相対パスになるようにしているので受け渡しは.blendファイルとこの指定したフォルダだけでよくなります
現状ではアセットライブラリ等の決められたライブラリフォルダにあるものも収集してしまいますが
何かの参考になればと思います