blender

I301 blender python


blenderのapiについてです。pythonで行います。
ドキュメントは以下にあります。
https://docs.blender.org/api/current/index.html

実行の方法

コンソールから

ターミナルから実行します。

ファイルから

テキストエディタから実行します。
システムコンソールに出力されます。

pythonファイルとして保存できます。
また読み込みもできます。

pyファイルを保存

VSCODEで変更

外部エディタ(VSCODE)で変更して反映できます。

作成物

pythonスクリプトですが、ほとんどの部分は生成AIで作成しています。

エクスポート

モデルをオブジェクトごとにfbx形式で出します。

import bpy

# エクスポート先のフォルダを指定
output_dir = "C:/work/ 任意のフォルダを指定"

# 選択したオブジェクトを取得
selected_objects = bpy.context.selected_objects

# オブジェクトの数だけ繰り返す
for obj in selected_objects:
    # オブジェクトの名前からファイル名を作成
    filename = f"{obj.name}.fbx"
    filepath = output_dir + filename

    # 一時的に他のオブジェクトを非表示にする
    for other_obj in selected_objects:
        if other_obj != obj:
            other_obj.hide_set(True)

    # オブジェクトをアクティブにする
    bpy.context.view_layer.objects.active = obj

    # FBXエクスポート
    bpy.ops.export_scene.fbx(
        filepath=filepath,
        use_selection=True
    )

    # 一時的に非表示にしたオブジェクトを再び表示する
    for other_obj in selected_objects:
        if other_obj != obj:
            other_obj.hide_set(False)

print("FBXファイルのエクスポートが完了しました。")

ボーン追加

vrmモデルにIKのターゲットボーンを追加します。

import bpy

# アーマチュアの名前
armature_name = "アーマチュア"

# IKターゲットにする新しいボーンの名前(引き出したボーン)
ik_bone_nameArr = ["IK_Bone1", "IK_Bone2", "IK_Bone3", "IK_Bone4"]

# IKの影響を受けるボーン(根本)の名前
chain_root_bone_nameArr = ["lower_arm.L", "lower_arm.R", "lower_leg.L", "lower_leg.R"]

# IK用の新しいボーンを作成して設定する関数
def addTarget(ik_bone_name, chain_root_bone_name):
    # アーマチュアオブジェクトを取得
    armature = bpy.data.objects.get(armature_name)

    if armature and armature.type == 'ARMATURE':
        # Editモードで新しいボーン(IK用)を追加
        bpy.context.view_layer.objects.active = armature
        bpy.ops.object.mode_set(mode='EDIT')

        # IK用の新しいボーンを作成して、元のボーンから引き出す
        chain_root_bone = armature.data.edit_bones.get(chain_root_bone_name)
        if chain_root_bone:
            ik_bone = armature.data.edit_bones.new(ik_bone_name)
            ik_bone.head = chain_root_bone.tail  # 既存ボーンの終端に一致させる
            ik_bone.tail = (
                chain_root_bone.tail[0],
                chain_root_bone.tail[1],
                chain_root_bone.tail[2] + 0.5,
            )
            ik_bone.use_connect = False  # 親子関係を持たせない

            print(f"'{ik_bone_name}' ボーンを作成しました。")
        else:
            print(f"ボーン '{chain_root_bone_name}' が見つかりません。")

        # オブジェクトモードに戻る
        bpy.ops.object.mode_set(mode='OBJECT')

        # IKコンストレイントの設定を追加するボーンのPose Boneを取得
        pose_bone = armature.pose.bones.get(chain_root_bone_name)
        if pose_bone:
            # IKコンストレイントを追加
            ik_constraint = pose_bone.constraints.new(type="IK")
            ik_constraint.target = armature
            ik_constraint.subtarget = ik_bone_name
            ik_constraint.chain_count = 2  # IKが影響するボーンの数

            # Y軸とZ軸をロック(IK制御下で回転できないようにする)
            pose_bone.lock_ik_y = True
            pose_bone.lock_ik_z = True

            # X軸の回転制限を設定(-180度から0度)
            pose_bone.use_ik_limit_x = True  # IK制限を有効化
            pose_bone.ik_min_x = -3.14159  # -180度(ラジアン)
            pose_bone.ik_max_x = 0  # 0度

            print(f"IKコンストレイントと回転制限を '{chain_root_bone_name}' に設定しました。")
        else:
            print(f"'{chain_root_bone_name}' のポーズボーンが見つかりません。")
    else:
        print(f"アーマチュア '{armature_name}' が見つかりません。")

# IKボーンとルートボーンを結びつける処理
for i in range(len(ik_bone_nameArr)):
    addTarget(ik_bone_nameArr[i], chain_root_bone_nameArr[i])

人体モデル(骨格)

VRoid studio

VRoid studioのVRMモデルを読み込んだ場合です。

import bpy

# アーマチュアの名前
armature_name = "Armature"

# IKターゲットにする新しいボーンの名前(引き出したボーン)
ik_bone_nameArr = ["IK_Bone1", "IK_Bone2", "IK_Bone3", "IK_Bone4"]

# IKの影響を受けるボーン(根本)の名前
chain_root_bone_nameArr = ["J_Bip_L_LowerArm", "J_Bip_R_LowerArm", "J_Bip_L_LowerLeg", "J_Bip_R_LowerLeg"]

# IK用の新しいボーンを作成して設定する関数
def addTarget(ik_bone_name, chain_root_bone_name):
    # アーマチュアオブジェクトを取得
    armature = bpy.data.objects.get(armature_name)

    if armature and armature.type == 'ARMATURE':
        # Editモードで新しいボーン(IK用)を追加
        bpy.context.view_layer.objects.active = armature
        bpy.ops.object.mode_set(mode='EDIT')

        # IK用の新しいボーンを作成して、元のボーンから引き出す
        chain_root_bone = armature.data.edit_bones.get(chain_root_bone_name)
        if chain_root_bone:
            ik_bone = armature.data.edit_bones.new(ik_bone_name)
            ik_bone.head = chain_root_bone.tail  # 既存ボーンの終端に一致させる
            ik_bone.tail = (
                chain_root_bone.tail[0],
                chain_root_bone.tail[1],
                chain_root_bone.tail[2] + 0.5,
            )
            ik_bone.use_connect = False  # 親子関係を持たせない

            print(f"'{ik_bone_name}' ボーンを作成しました。")
        else:
            print(f"ボーン '{chain_root_bone_name}' が見つかりません。")

        # オブジェクトモードに戻る
        bpy.ops.object.mode_set(mode='OBJECT')

        # IKコンストレイントの設定を追加するボーンのPose Boneを取得
        pose_bone = armature.pose.bones.get(chain_root_bone_name)
        if pose_bone:
            # IKコンストレイントを追加
            ik_constraint = pose_bone.constraints.new(type="IK")
            ik_constraint.target = armature
            ik_constraint.subtarget = ik_bone_name
            ik_constraint.chain_count = 2  # IKが影響するボーンの数

            # Y軸とZ軸をロック(IK制御下で回転できないようにする)
            pose_bone.lock_ik_y = True
            pose_bone.lock_ik_z = True

            # X軸の回転制限を設定(-180度から0度)
            pose_bone.use_ik_limit_x = True  # IK制限を有効化
#            pose_bone.ik_min_x = -3.14159  # -180度(ラジアン)
#            pose_bone.ik_max_x = 0  # 0度

            pose_bone.ik_min_x = 0  # 0度(ラジアン)
            pose_bone.ik_max_x = 3.14159  # 180度
            
            print(f"IKコンストレイントと回転制限を '{chain_root_bone_name}' に設定しました。")
        else:
            print(f"'{chain_root_bone_name}' のポーズボーンが見つかりません。")
    else:
        print(f"アーマチュア '{armature_name}' が見つかりません。")

# IKボーンとルートボーンを結びつける処理
for i in range(len(ik_bone_nameArr)):
    addTarget(ik_bone_nameArr[i], chain_root_bone_nameArr[i])

変更箇所:
armature_name = "Armature"
chain_root_bone_nameArr = ["J_Bip_L_LowerArm", "J_Bip_R_LowerArm", "J_Bip_L_LowerLeg", "J_Bip_R_LowerLeg"]

IKの回転角度は適宜変更します。
pose_bone.ik_min_x = 0 # 0度(ラジアン)
pose_bone.ik_max_x = 3.14159 # 180度
脚と腕だと変更する必要があります。

コメント