Skip to content

Instantly share code, notes, and snippets.

@katapad
Last active June 24, 2025 11:57
Show Gist options
  • Save katapad/2a1f5b86dcfd2972f907548ed4eff167 to your computer and use it in GitHub Desktop.
Save katapad/2a1f5b86dcfd2972f907548ed4eff167 to your computer and use it in GitHub Desktop.
import_alembic_from_browser.py
import bpy
import os
import glob
# 設定値(編集可能)
TEXTURE_DIR_NAME = "texture" # テクスチャディレクトリ名
ALEMBIC_DIR_NAME = "alembic" # Alembicディレクトリ名
ALEMBIC_SCALE = 0.001 # Alembicインポート時のスケール
FPS = 30 # フレームレート
IMAGE_EXTENSION = "png" # 画像の拡張子("png" または "jpg")
def import_alembic_with_texture(base_dir):
"""
Alembicファイルとテクスチャ連番画像をインポートする
ディレクトリ構造:
base_dir/
├── texture/ (連番PNG画像)
└── alembic/ (Alembicファイル)
"""
# 選択したディレクトリ名を取得
dir_name = os.path.basename(os.path.normpath(base_dir))
texture_dir = os.path.join(base_dir, TEXTURE_DIR_NAME)
alembic_dir = os.path.join(base_dir, ALEMBIC_DIR_NAME)
# ディレクトリの存在確認
if not os.path.exists(texture_dir):
raise Exception(f"テクスチャディレクトリが見つかりません: {texture_dir}")
if not os.path.exists(alembic_dir):
print(f"警告: Alembicディレクトリが見つかりません: {alembic_dir}")
# 1. 連番画像マテリアルの作成
print("連番画像マテリアルを作成中...")
# 画像を取得(拡張子を設定値から取得)
image_files = sorted([f for f in os.listdir(texture_dir) if f.lower().endswith(f'.{IMAGE_EXTENSION.lower()}')])
if not image_files:
raise Exception(f"textureディレクトリに{IMAGE_EXTENSION.upper()}画像が見つかりません")
num_frames = len(image_files)
print(f"見つかった画像: {num_frames}枚 ({IMAGE_EXTENSION.upper()}形式)")
# シーン設定の更新
scene = bpy.context.scene
scene.render.fps = FPS
scene.frame_start = 1
scene.frame_end = num_frames
print(f"シーン設定: FPS={FPS}, フレーム範囲=1-{num_frames}")
# マテリアル作成
mat = bpy.data.materials.new(name="AlembicTexture")
mat.use_nodes = True
nodes = mat.node_tree.nodes
nodes.clear()
# ノード作成
tex = nodes.new('ShaderNodeTexImage')
brightness = nodes.new('ShaderNodeBrightContrast')
curves = nodes.new('ShaderNodeRGBCurve')
shader = nodes.new('ShaderNodeBsdfPrincipled')
output = nodes.new('ShaderNodeOutputMaterial')
# 位置設定
tex.location = (-600, 0)
brightness.location = (-400, 0)
curves.location = (-200, 0)
shader.location = (0, 0)
output.location = (300, 0)
# Principled BSDFの設定
shader.inputs['Roughness'].default_value = 0.97
# 画像読み込み
first_image = os.path.join(texture_dir, image_files[0])
img = bpy.data.images.load(first_image)
img.source = 'SEQUENCE'
# Image Sequenceの設定
tex.image = img
tex.image_user.frame_duration = num_frames
tex.image_user.frame_start = 0
tex.image_user.frame_offset = -1
tex.image_user.use_auto_refresh = True
tex.image_user.use_cyclic = False
# ノード接続
links = mat.node_tree.links
links.new(tex.outputs['Color'], brightness.inputs['Color'])
links.new(brightness.outputs['Color'], curves.inputs['Color'])
links.new(curves.outputs['Color'], shader.inputs['Base Color'])
links.new(shader.outputs['BSDF'], output.inputs['Surface'])
print("マテリアル作成完了")
# 2. Alembicファイルのインポート
all_imported_objects = []
if os.path.exists(alembic_dir):
# Alembicファイルを検索
abc_files = glob.glob(os.path.join(alembic_dir, "*.abc"))
if abc_files:
print(f"\nAlembicファイルをインポート中...")
# 親となるEmptyオブジェクトを作成
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0, 0, 0))
parent_empty = bpy.context.active_object
parent_empty.name = f"{dir_name}_Parent"
parent_empty.empty_display_size = 0.0
for abc_file in abc_files:
print(f"インポート: {os.path.basename(abc_file)}")
# インポート前の既存オブジェクトを記録
existing_objects = set(bpy.data.objects)
# Alembicインポート
bpy.ops.wm.alembic_import(
filepath=abc_file,
scale=ALEMBIC_SCALE,
set_frame_range=True,
always_add_cache_reader=True
)
# 新しくインポートされたオブジェクトを特定
new_objects = list(set(bpy.data.objects) - existing_objects)
all_imported_objects.extend(new_objects)
# インポートされたオブジェクトの処理
mesh_count = 0
for obj in new_objects:
if obj.type == 'MESH':
mesh_count += 1
# オブジェクト名をディレクトリ名に変更
if len(new_objects) == 1:
obj.name = dir_name
else:
obj.name = f"{dir_name}_{mesh_count:03d}"
# マテリアルを適用
if len(obj.data.materials) > 0:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)
print(f" マテリアル適用: {obj.name}")
# すべてのインポートが完了した後の処理
print("\nTransformの調整とApply処理...")
# まず全選択を解除
bpy.ops.object.select_all(action='DESELECT')
for obj in all_imported_objects:
if obj.type == 'MESH':
# オブジェクトを選択
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# Transform適用
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
# 親を設定
obj.parent = parent_empty
# 選択解除
obj.select_set(False)
print(f" Transform適用完了: {obj.name}")
print("\nインポート完了!")
else:
print("Alembicファイルが見つかりませんでした")
# テスト用にプレーンを作成
bpy.ops.mesh.primitive_plane_add(size=2)
obj = bpy.context.active_object
obj.name = dir_name + "_TestPlane"
obj.data.materials.append(mat)
print("テスト用のPlaneを作成しました")
print(f"\n完了: テクスチャ{num_frames}枚、フレーム0から開始")
class SelectDirectoryOperator(bpy.types.Operator):
"""ディレクトリを選択"""
bl_idname = "select.directory"
bl_label = "Select Directory"
directory: bpy.props.StringProperty(
name="Directory Path",
description="Choose a directory",
default="",
subtype='DIR_PATH'
)
def execute(self, context):
print(f"選択されたディレクトリ: {self.directory}")
try:
# 選択されたディレクトリで import_alembic_with_texture を実行
import_alembic_with_texture(self.directory)
self.report({'INFO'}, "インポートが完了しました!")
return {'FINISHED'}
except Exception as e:
self.report({'ERROR'}, str(e))
return {'CANCELLED'}
def invoke(self, context, event):
# ファイルブラウザを開く
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
# スクリプト実行
if __name__ == "__main__":
# オペレーターを登録
bpy.utils.register_class(SelectDirectoryOperator)
# オペレーターを実行
bpy.ops.select.directory('INVOKE_DEFAULT')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment