0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Blenderで外部参照ファイルを収集するアドオン

Posted at

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()

プロパティエディタのシーンタブに追加するアドオンです
image.png
.blendファイルのある場所に このパネルで設定したフォルダを作成して
外部参照している画像や リンクしている.blendファイルをコピーします

ファイルパスの設定は相対パスになるようにしているので受け渡しは.blendファイルとこの指定したフォルダだけでよくなります

現状ではアセットライブラリ等の決められたライブラリフォルダにあるものも収集してしまいますが
何かの参考になればと思います

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?