【MotionBuilder】Python SDK 入門 第5回 『スクリプト例と解説』
この記事は、Python SDK 入門 の第5回目の記事です。
今回は様々なスクリプトの簡単な例を示していきます。
0. 書けることを増やすために
type()
を用いて扱っているデータの型を適宜確認するのは重要で、
「型(クラス)が分かればドキュメントで検索ができる」
「検索ができればメンバが分かる」
「メンバが分かれば実際に試せる」
「実際に試したらさらにデータが得られる」
「データが得られればその型を調べる」
「型が分かれば…(以下略)」
という過程でSDKの知識やスクリプトを書く経験を増やせるからです。
また、Property についてその種類を確認したいのは、Property = <some value>
という記述で Property を編集可能なのか、それとも値を設定する特別な関数が必要なのかが変わるからです。
# Read Only / Read Write Property の例
from pyfbsdk import*
model = FBModelCube("testcube")
# Show Property は Read write Property
model.Show
>>>
True
model.Show = False # Property は編集可能
# UniqueColorId Property は Read Only Property
model.UniqueColorId
>>>
FBColor(0.643137, 0, 0)
model.UniqueColorId = FBColor(0.2, 0, 0) # Property は編集不可、エラーが出る
>>>
Traceback (most recent call last):
File "<MotionBuilder>", line 1, in <module>
AttributeError: property of 'FBModelCube' object has no setter
それではスクリプト例を書いていきます。
以下の例では、インポート文(from pyfbsdk import*
)は省略しています。
1. 基本的な検索・データの取得
1.1. 名前でモデルを検索し取得
model_name = "target"
model = FBFindModelByLabelName(model_name)
print(model.Name)
>>>
target
FBFindModelByLabelName()
は基本のメソッドでよくお世話になります。
1.2. Scene 内の各データの取得
FBScene
クラスの各属性を使います。
lScene = FBSystem().Scene
for mat in lScene.Materials:
print(mat.Name) # 全マテリアル名の表示
for cam in lScene.Cameras:
print(cam.Name) # 全カメラ名の表示
1.3. Scene 内の全てのモデルの取得
def AccessChildrens(model:FBModel):
if len(model.Children) > 0:
for child in model.Children:
print(child.Name)
AccessChildrens(child)
AccessChildrens(FBSystem().Scene.RootModel)
FBModel
型を引数にとって、その子オブジェクトがあるのなら全ての子にアクセスする関数 AccessChildrens()
を定義。再帰を組めば親子関係の最下位オブジェクトまで探索できるため、実行時の引数には Scene 最上位のモデル FBSystem().Scene.RootModel
をとります。この RootModel
が返す最上位のモデルは実体が無く、Viewerで確認することはできません。
RootModel
1.4. 可視状態になっているか
Visibility Property が無効になっている場合、Shift + S
および Shift + H
の表示・非表示(Show Property の True/False)の切り替えに関わらず、Viewer では非表示になります。
bone = FBFindModelByLabelName("LeftArm")
print(bone.Visibility)
>>>
False # Shift + S 押してもViewerに表示されない
bone.Visibility = True # Viewerに表示され、Show Propertyで表示状態を切り替え可能になる
bone.Show = False # 非表示に
FBModel.IsVisible()
でも Visibility Property の True/False を確認できます。ドキュメントでは「Public Member Functions」の項目に並べられているのですが、どういう訳か属性のように用いないとエラーが出ます。
bone = FBFindModelByLabelName("Reference")
if bone.IsVisible:
print("the bone is visible\n")
>>>
the bone is visible
print(bone.IsVisible())
>>>
Traceback (most recent call last):
File "<string>", line 4, in <module>
TypeError: 'bool' object is not callable
1.5. 目的のクラス型なのか
確認する方法は3通りあります。
- Is(<ClassName>_TypeInfo())
- Is(<object>.TypeInfo)
- type(<object>) == pyfbsdk.<ClassName>
model = FBFindModelByLabelName("LeftHandThumb1")
# <class Name>_TypeInfo() function
print(model.Is(FBModel_TypeInfo()))
# FBComponent.TypeInfo attribute
print(model.Is(FBModelSkeleton.TypeInfo))
# type() method
print(type(model) == pyfbsdk.FBModel)
>>>
True
True
False
type()
は継承関係を考慮しません。
1.6. ベクトルの取得
属性で直接得る場合とPropertyListを介して得る場合の2通りあります。
bone = FBFindModelByLabelName("Mia:LeftArmRoll")
# 属性から得る
print(bone.Rotation)
>>>
# PropertyListから得る
rotationProp = bone.PropertyList.Find("Lcl Rotation")
print(rotationProp)
>>>
ただし、この方法で得るモデルの座標はローカル座標です。グローバル座標は GetVector()
を使います。このメソッドは FBVector3d 型変数を引数にとり、その変数に取得した座標を保存します。したがって、ユーザーは事前にその変数を宣言しておく必要があります。
model = FBFindModelByLabelName("Mia:LeftArmRoll")
vec = FBVector3d()
model.GetVector(vec, FBModelTransformationType.kModelRotation) # Global Rotation
print(vec)
model.GetVector(vec, FBModelTransformationType.kModelTranslation) # Global Translation
print(vec)
model.GetVector(vec, FBModelTransformationType.kModelTransformation)
print(vec)
>>>
FBVector3d(-89.998, 0.00234162, -0.00645417)
FBVector3d(30.4288, 144.19, -2.56136)
FBVector3d(30.4288, 144.19, -2.56136)
kModelTranslation
と kModelTransformation
の違いはよく知りません。
また、第2引数に False
を指定するとローカル座標が得られます(デフォルトは True です)。
model.GetVector(vec, FBModelTransformationType.kModelRotation, False)
print(vec)
model.GetVector(vec, FBModelTransformationType.kModelTranslation, False)
print(vec)
model.GetVector(vec, FBModelTransformationType.kModelTransformation, False)
print(vec)
>>>
FBVector3d(-0.824261, 2.57693e-05, 7.33129e-06)
FBVector3d(33.0882, 9.90851e-06, 1.47323e-05)
FBVector3d(33.0882, 9.90851e-06, 1.47323e-05)
2. Character 関連
2.1. Characterの取得
2通りあります。
# Scene内のCharacterのリストを得て、イテレータで指定
chara = FBSystem().Scene.Characters[0]
# 現在アクティブ状態(≒選択状態)のCharacterを得る
chara = FBApplication().CurrentCharacter
後者については、Character Controls Window が表示されていない場合(Scripting Layout など)、Scene Browser 等で character が選択状態になっていないと FBCharacter型オブジェクトを返してくれないので注意。characterが得られたかどうかの条件分岐については以下の通り。
# Charactersを用いた場合
charaList = FBSystem().Scene.Characters
if len(charaList) == 0:
print("cannot get character")
# CurrentCharacterを用いた場合
chara = FBApplication().CurrentCharacter
if chara == None:
print("cannot get character")
Scene 内に character が一つも無い場合、Characters[0]
と書くと index out of range エラーが出てしまうので、まずはイテレータを使わずに character のリストを取得し、その要素数が0かどうかの判断を入れるとよいかと思います。
2.2. キャラクタライズ
キャラクタライズを実行するにはまずボーンの Mapping(割り当て)の作成が必要です。FBCharacter
型のPropertyListより、「~Link」Property に対応する slot へボーンのモデルを登録します。
# Character作成
chara = FBCharacter("test")
# Mappingの作成
skeletonMap = {
# Property名:str とボーン名:str の辞書
}
# 各Propertyへのボーンの登録
for key in skeletonMap.keys():
LinkProp = chara.PropertyList.Find(key)
bone = FBFindModelByLabelName(skeletonMap[key])
LinkProp.append(bone)
キャラクタライズを実行します。
# キャラクタライズ実行
chara.SetCharacterizeOn(True)
# キャラクタライズ結果の判断
if chara.GetCharacterizeError() == "":
FBMessageBox("Message", "Characterize succeeded!", "OK")
else:
FBMessageBox("Caution", chara.GetCharacterizeError(), "OK")
GetCharacterizeError()
は直前のキャラクタライズ処理のエラー結果(文字列)を返します。キャラクタライズに成功した場合はエラーが無いので空文字列が返されます。
2.3. Characterに登録されたボーンの取得
2通りあります。
-
FBBodyNodeId
を用いる方法# FBBodyNodeIdのvalues属性はenumとIdの辞書を返す for id in FBBodyNodeId.values.values(): bone = chara.GetModel(id) if bone != None: print(bone.Name)
よく似た名前のIdに
FBSkeletonNodeId
とFBBodyPartId
がありますが、FBSkeletonNodeId
は Actor の各部へのアクセスと MarkerSet への登録、FBBodyPartId
はControl Rig 各部へのアクセスに使います。
-
Property を用いる方法
for prop in chara.PropertyList: if prop.Name.endswith("Link") and len(prop) > 0: print(prop[0].Name)
2.4. characterにリンクされたメッシュの取得
より正確には、character に登録されたボーンに対してウェイトが塗られたメッシュを取得します。
chara = FBCharacter("test")
meshList = FBModelList()
chara.GetSkinModelList(meshList)
GetSkinModelList()
は引数の FBModelList
型インスタンスに対して、character のスケルトンに結びついたメッシュモデルを登録していきます。
3. その他
3.1. アニメート関連
以下の3つを使います。
- IsAnimatable() :アニメート可能か
- IsAnimated() :アニメートされているか
- SetAnimated(bool) :アニメートの切り替え
model = FBFindModelByLabelName("Face")
for prop in model.PropertyList:
if prop.IsAnimatable():
if not prop.IsAnimated():
prop.SetAnimated(True)
SetAnimated(True)
が「アニメートする」であるのに対し、SetAnimated(False)
は "remove curves"、すなわち「FCurves を取り除く」という処理です。UI上で『A』ボタンを押した時に表示される警告にあるように、SetAnimated(False)
はその Property のアニメーションデータを全て失うので注意。
3.2. グループ化
例えば、FBGetSelectedModels()
で現在選択中のモデルを全て取得してみます。
mList = FBModelList()
FBGetSelectedModels(mList)
for model in mList:
print(model.Name)
>>>
Mia_right_leg
Mia_left_leg
Mia_head
Mia_body
Mia_left_pad1
Mia_left_pad2
Mia_left_pad3
Mia_right_pad1
Mia_right_pad2
Mia_right_pad4
Mia Hair
Mia_R_eye
Mia_L_eye
続いて、グループを作成し、モデルを登録します。
この時 第4回 で触れた ConnectSrc()
を使います。
grp = FBGroup("meshes")
for model in mList:
grp.ConnectSrc(model) # グループへ追加
for elm in grp.Items: # Items でグループ内要素のリストを得る
print(elm.Name)
>>>
Mia_right_leg
Mia_left_leg
Mia_head
Mia_body
Mia_left_pad1
Mia_left_pad2
Mia_left_pad3
Mia_right_pad1
Mia_right_pad2
Mia_right_pad4
Mia Hair
Mia_R_eye
Mia_L_eye
3.3. シェイプキーの取得
メッシュの Geometry
属性から取得します。
meshList = FBModelList()
chara.GetSkinModelList(meshList)
for mesh in meshList:
geo = mesh.Geometry
for i in range(geo.ShapeGetCount()):
name = geo.ShapeGetName(i)
prop = mesh.PropertyList.Find(name)
prop.SetAnimated(True)
シェイプキーに対応する Property をインデックスで指定して取得する FBGeometryクラスのメンバ関数はありません(名前で取得するものはありますが)。幸いシェイプキー名はそのまま Property名でもあるので、メッシュの PropertyList で Find()
関数を用いればシェイプキーの Property が取得できます。
3.4. Asset Browserよりアセット作成
FBCreateObject()
を使います。
def ApplyShaders():
# get all mesh of character
chara = FBApplication().CurrentCharacter
meshList = FBModelList()
chara.GetSkinModelList(meshList)
for mesh in meshList:
shader = FBCreateObject('Browsing/Templates/Shading Elements/Shaders', 'Flat', 'myShader')
mesh.ShadingMode = FBModelShadingMode.kFBModelShadingFlat
mesh.Shaders.append(shader)
このスクリプトは character に結び付いたメッシュを全て取得し、Flat シェーダーを適用するものです。
次回
今回は、簡単なスクリプトの解説と使用例を紹介しました。ここで紹介しきれなかったスクリプト例については、bin/config/Scripts ディレクトリにある多くのサンプルを参照ください。
次回は一度 SDK の話題から離れて、便利な VScode の拡張機能について紹介します。
それでは、今回はここまで。最後までお読みくださりありがとうございました。
Discussion