MediaPipeからBlenderへ - ⑤ ボーンの回転をスクリプトで

From MediaPipe to Blender - Scripting the rotation of a bone

ベビーダンスのアニメーションを更新する前に、ボーンの回転をスクリプトで行うのに苦労したので、忘れないうちにメモしておきます。

ボーンのテールが赤い星のところに来るように、ボーンを回転させたいとします。関係する3点の座標は図の通りです。

アーマチュア・オブジェクトの回転

from mathutils import Vector, Matrix

obj = bpy.context.object
currentVec = Vector((5.0775, 1.0304, -0.81277)) - Vector((3.4182, 0.45823, -1.4133))
targetVec = Vector((5.126, -0.11371, -1.739)) - Vector((3.4182, 0.45823, -1.4133))
Q = currentVec.rotation_difference(targetVec)
obj.rotation_quaternion = Q

オブジェクトの回転があっさりうまく行ったので、ポーズボーンも同じノリで…

ポーズ・ボーンの回転

…と思いきや、これがドツボハマりで悪戦苦闘しました。ChatGPT先生に質問したら、こんな答が返ってきました。

Blenderにおけるオブジェクト回転とポーズボーン回転の違いは、オブジェクト回転は3D空間におけるオブジェクト全体の向きを指し、ポーズボーン回転はアーマチュア内の単一のボーンの向きを指すことです。

オブジェクトのrotation_quaternionプロパティを設定するとき、それはグローバル空間でのオブジェクト全体の向きを設定しているのです。これは、オブジェクトがそれ自身の原点を中心に回転することを意味し、アーマチュアの原点と同じである場合もあれば、そうでない場合もあります。

一方、ポーズモードでポーズボーンのrotation_quaternionプロパティを設定すると、その親ボーンに対するボーンの向きを設定することになります。つまり、ボーンは自身の原点を中心に回転し、その原点は親ボーンの原点と相対的になります。

ですから、ポーズモードでポーズボーンの回転を設定したい場合は、アーマチュアのボーン間の親子関係を考慮する必要があります。

rotation_difference関数は、2つの回転の差をクォータニオンとして計算します。この差分は、2つの回転が同じ座標空間にあると仮定しているため、ポーズボーンの回転を設定する際には直接適用できない場合があります。ポーズボーンの場合、回転は親ボーンに対する相対的なものなので、いくつかの追加計算を適用する必要があります。

どうやら、行列の出番のようです。StackExchangeに、まさしくこの問題を解くためのコードを載せてくれた人がいました。

pb = bpy.context.active_pose_bone

# currentVec, targetVecは上記で設定したものを使う
Q = currentVec.rotation_difference(targetVec)

# 3個のマトリクス(行列)の掛け算
M = (
    Matrix.Translation(pb.head) @
    Q.to_matrix().to_4x4() @
    Matrix.Translation(-pb.head)
    )

# 元の行列にMを掛けたものを代入
pb.matrix = M @ pb.matrix

次回はいよいよMediaPipeのランドマーク・データからアーマチュアを動かしてみます。



関連記事