夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

BlenderPythonでアニメーションから差異がないキーフレームを削除する

本日はBlenderPython枠です。

 先日BlenderでアニメーションをベイクすることでIKなどボーン本体にキーフレームアニメーションが登録されていない場合でもキーフレームを登録させました。

 アニメーションをベイクすることですべてのフレームのキーフレームアニメーションが登録されます。

 しかしながらこれは、可読性の悪化やさらなる修正の差異の障壁となる場合があります。

 今回はPythonを使用してフレーム間でキーの変異がない場合はキーフレームアニメーションの登録の解除を粉う実装を行っていきます。

〇環境

・Windows11PC

・Blender4.1

Pythonを用いたキーフレームの削除

まずはPythonを用いてキーフレームの登録の解除を行います。

今回は次のようなシーンを用意しました。 250フレームのキューブのアニメーションです。

キーフレームを削除するためにはアクションを取得して削除する方法がとられます。

具体的には以下のようなコードになります。

import bpy

# 現在選択しているオブジェクトを取得
obj = bpy.context.active_object

# 削除したいフレーム番号を指定
frame_number = bpy.context.scene.frame_current

# キーフレームを削除
if obj.animation_data and obj.animation_data.action:
    action = obj.animation_data.action
    for fcurve in action.fcurves:
        keyframe_points = fcurve.keyframe_points
        for keyframe in keyframe_points:
            if keyframe.co.x == frame_number:
                keyframe_points.remove(keyframe)
                break

# シーンを更新
bpy.context.view_layer.update()

これを実行することで現在のフレームのキーフレームが削除されます。

〇前後で変異がないキーフレームの削除

続いて前後のフレームで値の変化がない場合のフレームを削除するには以下のように実装します。

import bpy

# 現在選択しているオブジェクトを取得
obj = bpy.context.active_object

# 許容する変異の範囲を設定
tolerance = 0.001

# キーフレームを削除
if obj.animation_data and obj.animation_data.action:
    action = obj.animation_data.action
    for fcurve in action.fcurves:
        keyframe_points = fcurve.keyframe_points
        i = 1
        while i < len(keyframe_points) - 1:
            prev_keyframe = keyframe_points[i - 1]
            current_keyframe = keyframe_points[i]
            next_keyframe = keyframe_points[i + 1]
            
            # 前後のフレームの値を比較
            if abs(prev_keyframe.co.y - current_keyframe.co.y) < tolerance and abs(next_keyframe.co.y - current_keyframe.co.y) < tolerance:
                keyframe_points.remove(current_keyframe)
            else:
                i += 1

# シーンを更新
bpy.context.view_layer.update()

〇直線運動などのキーフレームの削除

次に直線運動時など前後で保管によって再現されキーフレームが不要な場合の処理は以下のようになります。

import bpy

# 現在選択しているオブジェクトを取得
obj = bpy.context.active_object

# 許容する変異の範囲を設定
tolerance = 0.001

# キーフレームを削除
if obj.animation_data and obj.animation_data.action:
    action = obj.animation_data.action
    for fcurve in action.fcurves:
        keyframe_points = fcurve.keyframe_points
        i = 1
        while i < len(keyframe_points) - 1:
            prev_keyframe = keyframe_points[i - 1]
            current_keyframe = keyframe_points[i]
            next_keyframe = keyframe_points[i + 1]
            
            # 補間方法を取得
            interpolation = current_keyframe.interpolation
            
            if interpolation == 'LINEAR':
                # リニア補間の場合の処理
                prev_slope = (current_keyframe.co.y - prev_keyframe.co.y) / (current_keyframe.co.x - prev_keyframe.co.x)
                next_slope = (next_keyframe.co.y - current_keyframe.co.y) / (next_keyframe.co.x - current_keyframe.co.x)
                
                if abs(prev_slope - next_slope) < tolerance:
                    keyframe_points.remove(current_keyframe)
                else:
                    i += 1
            elif interpolation == 'BEZIER':
                # ベジェ補間の場合の処理
                # ベジェ補間の制御点を考慮して不要なキーフレームを削除するロジックを追加
                # ここでは簡略化のため、リニア補間と同様の処理を行います
                prev_slope = (current_keyframe.co.y - prev_keyframe.co.y) / (current_keyframe.co.x - prev_keyframe.co.x)
                next_slope = (next_keyframe.co.y - current_keyframe.co.y) / (next_keyframe.co.x - current_keyframe.co.x)
                
                if abs(prev_slope - next_slope) < tolerance:
                    keyframe_points.remove(current_keyframe)
                else:
                    i += 1
            else:
                # その他の補間方法の場合の処理
                i += 1

# シーンを更新
bpy.context.view_layer.update()

閾値を変えることで柔軟な補完に対応します。

以上でPythonを使用してアニメーションからキーフレームを削除できました。

本日は以上です。