MotionBuilder Scripts - 階層を保ったモデルのコピー
目標
Scene内の複数のモデルを、その階層を保ったままスクリプトでコピーするメソッドを作成する。
使用例
Character のスケルトン構造をコピー
解説
モデルのコピー
MotionBuilder のUI上におけるコピー(Ctrl+C → Ctrl+V)に対応するメソッドとして、FBModel.Clone()
が SDK で提供されている。
公式リファレンスより
FBModel
およびその継承クラス(FBModelNull
やFBModelSkeleton
)でこのメソッドを用いれば、モデルのコピーができる。
model = FBModelCube("test cube")
model_clone = model.Clone() # コピー
print("%s : %s" % (model.Name, model))
print("%s : %s" % (model_clone.Name, model_clone))
# >>>
# test cube : <pyfbsdk.FBModelCube object at 0x0000023548166AC0>
# test cube 1 : <pyfbsdk.FBModel object at 0x00000235481B3990>
上記例では、コピーされたモデルの型が元のモデルから変化しているが、FBModelCube
特有のもので動作には問題ないと思われる[1]。
また、コピーされたモデルはデフォルトでUIに表示される。
print(model.Show)
print(model_clone.Show)
# >>>
# False
# True
ペアレント化と階層のコピー
FBModel.Parent
プロパティはそのモデルの親にあたるモデルを保持する。
公式リファレンスより
このプロパティは Read Write Property であるから、親子関係を構成したい場合は「子」のモデルの Parent
プロパティに「親」のモデルを格納すればよい。
cube_parent = FBModelCube("Parent Cube")
cube_child = FBModelCube("Child Cube")
cube_child.Parent = cube_parent
作成された親子関係
階層ごとモデルをコピーする方法として、メソッド「親モデルとそのコピーを引数に取り、その子モデルを参照して作成したコピーを親モデルのコピーにペアレント化」を再帰的に実行する方法が考えられる。
# 子モデルを再帰的にコピーする、メソッド
def CloneChildModels(model_parent: FBModel, model_parent_clone: FBModel) -> None:
if len(model_parent.Children) > 0:
for model_child in model_parent.Children:
# Clone およびペアレント化
model_child_clone = model_child.Clone()
model_child_clone.Parent = model_parent_clone
CloneChildModels(model_child, model_child_clone)
model_root = FBFindModelByLabelName("ルートのモデル名")
model_root_clone = model_root.Clone()
CloneChildModels(model_root, model_root_clone)
先程作成した Cube の階層について実行すれば、以下のようにコピーされた階層が得られる。
コピーされた Cube 階層
Transform のコピー
前節の方法では階層のコピーに成功したが、モデルの座標(Transform)はコピーされない。
モデルの Transfrom を取得・設定するには、FBModel.GetMatrix()
および FBModel.SetMatrix()
メソッドを使用する。
公式リファレンスより
公式リファレンスより
Transform の取得の際は引数に FBMatrix
型インスタンスを使用するので、予め作成しておく。Transform 設定時に、再度このインスタンスを引数に取ればよい。
matrix_tr = FBMatrix()
cube_parent.GetMatrix(matrix_tr) # コピー元の Tranform 取得
cube_parent_clone = FBFindModelByLabelName("Parent Cube 1")
cube_parent_clone.SetMatrix(matrix_tr) # Tranform 設定
cube_child.GetMatrix(matrix_tr) # コピー元の Tranform 取得
cube_child_clone = FBFindModelByLabelName("Child Cube 1")
cube_child_clone.SetMatrix(matrix_tr) # Tranform 設定
Namespace 付きのモデルのコピーと名前の処理
namespace を持つモデルを FBModel.Clone()
でコピーすると、namespace名に接尾辞が付く。
namespace を持つモデルを"スクリプトで"コピーした場合
ところで、MotionBuilder におけるコンポーネントの名前は、以下の3種類ある。
名前の種類 | 概要とSDKでの扱い |
---|---|
Name | group と namespace を省いたオブジェクト名(例:「Hips」)Name プロパティで取得。 |
LabelName | namespace を含めたオブジェクト名(例:「Mia:Hips」)LongName プロパティで取得。 |
FullName | group と namespace を含めたオブジェクト名(例:「Model::Mia:Hips」)FullName プロパティで取得。 |
Scene内モデルの取得でよく使用される(と思う)メソッド FBFindModelByLabelName()
の「LabelName」は、まさに上記の LabelName
のことである。 namespace 付きのモデルを扱う場合は、上記の使い分けに注意すること。
MotionBuilder のUI上では、モデルの名前を編集する際に namespace を直接指定することはできない。一方、SDK からは LongName
プロパティを編集することで、直接 namespace の指定ができる。
model1 = FBFindModelByLabelName("TEST 1:Parent Cube")
model2 = FBFindModelByLabelName("TEST 2:Child Cube")
model1.LongName = "Parent Cube" # namespace 無し
model2.LongName = "TEST 3:Child Cube" # namespace あり
スクリプトによる namespace付きのモデル名の処理
新規の namespaceを使って名前を変更した場合は、FBNamespace
型のオブジェクトが作成され、Scene に追加される。一方で、namespace無しの名前を設定したとして、その namespace をもつモデルが無くなったとしても、namespace 自体は Scene から削除されない。
Namespace に関するメソッドなど
FBNamespace
クラスおよび FBScene
クラスには、namespace に関する処理を行うメソッドがある。ただし、処理に少々癖がある[2][3]ので、公式リファレンスの説明をよく読んで、実際の動作を確認していただきたい。
今回は、FBComponent.ProcessObjectNamespace()
を使用する。
公式リファレンスより
単純に namespace を更新したい場合は、以下のように用いる。
# モデルの namespace名を返すメソッド
def GetNamespaceStr(model: FBModel) -> str:
colon_index = model.LongName.find(":")
if colon_index != -1:
return model.LongName[0:colon_index]
else:
return ""
model = FBFindModelByLabelName("TEST:Parent Cube")
namespace_original = GetNamespaceStr(model) # 元の namespace の取得
namespace_updated = "TEST_updated"
if namespace_original:
model.ProcessObjectNamespace(
FBNamespaceAction.kFBReplaceNamespace,
namespace_original,
namespace_updated,
False
)
第3引数 pReplaceTo
に指定した namespace名が Scene に無い場合は、その名前を使って自動で FBNamespace
オブジェクトが作成される。
備考
スクリプトを使わない場合は、該当のモデルを全選択し、「Ctrl+C → Ctrl+V」を実行すれば、階層と Transform を保持したままコピーができる。このとき、namespace も必要以上作成されることは無い。今回の記事で扱ったのは、あくまでスクリプトからこの処理を行いたい場合である。
P.S.もし SDK に同様の処理を行うメソッドが既にあれば教えてください。
ソースコード
Discussion