モーションビルダースクリプトTips
2012/4/9
Tag: MotionBuilder,python,script
久々に投稿のタイミングが回ってきましたー。お久しぶりです。アニメーションチームTDの上原です。
以前投稿時はSoftimageなTDだったのですが、諸事ありましてアニメーションチーム専属になりましたー。
CGといえばアニメーション派だった私にとってこれは願ってもないチャンス!と思いきや扱うソフトが増えててんてこ舞いに・・・。最近はメインでMotionBuilder周りの開発、あとはプロジェクトによってSoftimageかMayaです。複数のソフトの仕様に接しているといろんな発見があって新鮮で、あるソフトが持っている機能を別のソフトで実現するにはどんなアプローチで実装すればいいだろう考えるだけでも楽しいです。
せっかくなのでちょっと求人もしちゃお。
スクリプトスキルを高めたい方はぜひDFのアニメーションチームにTDとして応募したらいいと思いますよ。
たとえばこんな人。
・もともとCGアニメーターやってたけど、ちょっとスクリプト書き出したらそっち頼まれる機会が増えて
いろんな意味でハマってしまった人。
・モーションデザイナーをちょっと休憩してプログラミングスキルを身につけたい人。
・ゲーム屋さんでテクニカルアーティストやってるけど、映像系の知識もほすぃーっていう人。
・ドMな人、もしくはドSな人。(良い意味で)
映画もゲームも大作に携われるのはデカイですし、何よりフリーダムですよ。学生さんでも大歓迎ー。
はい。
というわけで、アニメーションチームのメインツールである、MotionBuilderのスクリプトネタをご紹介しようと思います。
まず、どのソフトでもそうですが、ワークフロー上の何らかのルールに則った作業を自動化したいというケースは多くて、SoftimageやMayaなどであればコマンドの履歴が出ますから、一通り手動で作業したコマンドの履歴をコピペして、ちょっと書き換えれば何とかなってしまうこともあります。ところがMotionBuilderはそれを許しませんっ。コマンドの履歴なんてありませんからね。とっかかりとしては非常にやりにくいソフトだと思われます。
「何らかの作業を自動化する」場合、
大体、前処理(データの状態を知る)、メイン処理(作成する・変更する・削除する)、後処理(整理する・記録する)の3つに分かれるかと思っています。どういうことかというと、
たとえば、MotionBuilderのシーン中から何らかの条件を満たすものを検索する前処理、見つけたら何らかの変更を加えるメイン処理、そして実行結果をサーバー上に記録しておく後処理といった具合です。
もちょっと具体的な例でいうと、
「キャラのデータがまだない!」そんなときは、とりあえず仮の別キャラで作業しといて、あとで正しいキャラのデータに差し替えるかと思います。
初めから仕込まれている補助骨用コンストレインの様なものはいいとして、それ以外に、別途デザイナーが作業用にコンストレインを組んでいたりすると、差し替えた後に手動でまたコンストレインをつけ直させるなんて無駄です。何十カットもあったら最悪です。差し替えをスクリプトで自動化する際に、「もし勝手に作ったコンストレインがあったらつけ直す」など、状況に応じて対応できると便利ですよね。さらに、ツールを使用した時の詳細情報をログとしてサーバーに出力しておけば、不具合の報告があった際にそれがいつから不具合が出ているのか、特定のキャラのみで発生する不具合なのかなど原因を絞り込みやすくなります。ということは無駄な検証、情報収集をせずに済むというメリットがあります。
と、ずいぶん前置きが長くなってしまいましたが、
今回はその一番初めの工程、「前処理(データの状態を知る)」 に着目してスクリプトを紹介してみたいと思います。
目次
・シーン内のアイテムを取得する
・グループ・フォルダの中身を取得する
・あるアイテムがどのグループ・フォルダに所属しているか知る
・選択しているシーン内のアイテムを取得する
・プロパティの値を取得する
・コンストレインで使用しているモデルを取得する
・モデルを拘束している、もしくはモデルを参照しているコンストレインを取得する
※本記事中のコードは全てMotionBuilder2010(windows7,64bit)でのみ動作確認を行っています。他の環境では動作に違いがあるかもしれませんのでご注意ください。
シーン内のアイテムを取得する
まずは、シーンから何かを取得する例です。
■名前を指定してモデルを取得
from pyfbsdk import * a = FBFindModelByName("Cube") print a.Name
シーンにCubeがあればこれで取得することができます。
FBFindModelByName() は pyfbsdkモジュール で既に準備してある便利なファンクションです。
しかし、取得できるのはモデル限定です。グループやコンストレインを取得することはできません。
グループやコンストレインを取得したい場合は次に説明するコードで取得できます。
■リストの中から指定した名前にマッチするオブジェクトを取得
import re from pyfbsdk import * def get_find_objects_by_name(name, items): ptn = re.compile(name) result = [] for item in items: if ptn.search(item.Name): result.append(item) return result def print_names(comment, items): for item in items: print comment, item.Name a = get_find_objects_by_name("", FBSystem().Scene.Groups) print_names("Groups:", a) a = get_find_objects_by_name("Position", FBSystem().Scene.Constraints) print_names("Constraints:", a) a = get_find_objects_by_name("^(?!Producer)", FBSystem().Scene.Cameras) print_names("Cameras:", a)
get_find_objects_by_name(name, items) について
第1引数(name)に指定する名前で検索します。書式には正規表現を指定することができます。
第2引数(items)に指定するリストの中から検索します。ここを状況に応じて変えれば汎用的に使うことが可能です。
上記コードはグループとコンストレインとカメラのリストからそれぞれ取得する例ですが、
他にフォルダやテクスチャーなどでも可能です。↓
FBSystem().Scene.Folders FBSystem().Scene.Shaders FBSystem().Scene.Textures FBSystem().Scene.VideoClips FBSystem().Scene.Materials FBSystem().Scene.Characters
グループ・フォルダの中身を取得する
今度はグループやフォルダに登録されているモデルを取得する方法です。
適当なグループを作り、Cube等のモデルを追加してから実行してください。
import re from pyfbsdk import * def get_find_objects_by_name(name, items): ptn = re.compile(name) result = [] for item in items: if ptn.search(item.Name): result.append(item) return result def print_names(comment, items): for item in items: print comment, item.Name def get_members_of_(target): result = [] for item in target.Items: result.append(item) return result grps = get_find_objects_by_name("", FBSystem().Scene.Groups) a = get_members_of_(grps[0]) print "Group:", grps[0].Name print_names("Group item:", a)
get_members_of_(target) について
グループやフォルダの中身は .Items で取得することができます。(大文字小文字の区別がありますからご注意ください)
中身から名前を指定して取得したい場合は、 get_members_of_(target) のかわりに
get_find_objects_by_name(name, items) を使い回せば名前でフィルタリングしつつ取得できますね!
grps = get_find_objects_by_name("", FBSystem().Scene.Groups) a = get_find_objects_by_name("^Cube", grps[0].Items) print "Group:", grps[0].Name print_names("Group item:", a)
あるアイテムがどのグループ・フォルダに所属しているか知る
今度は逆に、モデル等がどのグループ・フォルダに含まれているかを調べる方法です。
import re from pyfbsdk import * def get_find_objects_by_classname(name, items): ptn = re.compile(name) result = [] for item in items: if ptn.search(item.ClassName()): result.append(item) return result def print_names(comment, items): for item in items: print comment, item.Name cube = FBFindModelByName("Cube") a = get_find_objects_by_classname("FBGroup",cube.Parents) print_names("Owner groups:", a) a = get_find_objects_by_classname("FBFolder",cube.Parents) print_names("Owner folders:", a)
get_find_objects_by_classname(name, items) について
get_find_objects_by_name(name, items) を名前ではなくクラス名で判定できるよう
に書き換えました。コードはほとんど一緒です。
あるアイテムが属しているグループやフォルダは .Parents で取得することができます。
選択しているシーンアイテムを知る
スクリプトの処理対象をユーザーに指定してもらうケースなどで、選択の有無を判定する方法です。
Cube や camera をシーン上に作成後、選択して実行してみてください。
from pyfbsdk import * def print_names(comment, items): for item in items: print comment, item.Name models = FBModelList() FBGetSelectedModels(models) print_names("Selected:", models)
FBGetSelectedModels(FBModelList) は pyfbsdkモジュール で既に準備してある便利なファンクションです。
上のコードでは、選択されているモデルを models に格納してくれています。
しかし、取得できるのはモデル限定です。グループやコンストレインを取得することはできません。
そういう場合は次に説明するコードで取得できます。
from pyfbsdk import * def get_selected_objects(items, selected=True): result = [] for item in items: if item.Selected == selected: result.append(item) return result def print_names(comment, items): for item in items: print comment, item.Name a = get_selected_objects(FBSystem().Scene.Groups) print_names("Selected:", a) a = get_selected_objects(FBSystem().Scene.Groups, False) print_names("Not selected:", a) a = get_selected_objects(FBSystem().Scene.Folders) print_names("Selected:", a)
get_selected_objects(items, selected=True) について
選択の有無は、.Selected で判定することができます。
第1引数で指定するリストの中から判別します。
第2引数は通常指定しなくてもいいですが、
False を渡すことで、逆に選択していないものだけを取得できるようになっています。
プロパティの値を取得する
続いて、今度はプロパティの値を取得してみます。プロパティというのは、SIのパラメータ、Mayaでいうアトリビュートです。
Camera をシーン上に作成して実行してください。
from pyfbsdk import * cam = FBFindModelByName("Camera") print cam.PropertyList.Find("Field Of View").Data print cam.PropertyList.Find("Lcl Translation").Data print cam.PropertyList.Find("ShowTimeCode").Data print cam.PropertyList.Find("BackgroundColor").Data
Find(PropertyName) でプロパティ名を直接指定し取得できます。
ただ、プロパティ名がわからないと指定しようがないわけで、
どんなプロパティを持っているかは一覧にして確かめてみるといいです。
from pyfbsdk import * def print_names(comment, items): for item in items: print comment, item.Name cam = FBFindModelByName("Camera") print_names("Property:",cam.PropertyList)
コンストレインで使用しているモデルを取得する
コンストレインが拘束している、もしくは参照しているモデルを取得する方法です。
適当なモデルいくつかと Positionコンストレイン を作成してから実行してください。
from pyfbsdk import * def print_names(comment, items): for item in items: print comment, item.Name def get_find_objects_by_name(name, items): ptn = re.compile(name) result = [] for item in items: if ptn.search(item.Name): result.append(item) return result def get_registered_models(cns): result = [] for grp_i in range(cns.ReferenceGroupGetCount()): for model_i in range(cns.ReferenceGetCount(grp_i)): result.append(cns.ReferenceGet(grp_i, model_i)) return result constraints = get_find_objects_by_name("Position", FBSystem().Scene.Constraints) a = get_registered_models(constraints[0]) print_name("", a)
get_registered_models() について
コンストレインは最低でも拘束する側とされる側の2つが必要です。Positionコンストレイン を例にすると、
「Constrained Object」 と 「Source」 です。
この場合、 ReferenceGroupGetCount() を使って2という値が返ってきます。つまり、Positionコンストレイン を構成する要素として、2つのグループが存在するということです。
また、2つ目の要素である
「Source」 は、「Source 1」 「Source 2」 「Source 3」 の様に参照先を増やすことができます。この場合、 ReferenceGetCount(GroupIndex) を使うと3という値が返ってきます。これは3つのモデルを参照しているということです。
最後に、 ReferenceGet(GroupIndex, ModelIndex) を使って
すべてのグループで使用している、すべてのモデルを取得しています。
あるモデルを拘束している、もしくは参照しているコンストレインを取得する
今度は上のコードとは逆で、モデルからコンストレインを取得する方法です。
Cube と適当なコンストレインを作成してから実行してください。
from pyfbsdk import * def print_names(comment, items): for item in items: print comment, item.Name def get_constraints_by_source(model): result = [] for i in range(model.GetDstCount()): if model.GetDst(i): if model.GetDst(i).ClassName().startswith("FBConstraint"): result.append(model.GetDst(i)) return result def get_constraints_by_destination(model): result = [] for i in range(model.GetSrcCount()): if model.GetSrc(i): if model.GetSrc(i).ClassName().startswith("FBConstraint"): result.append(model.GetSrc(i)) return result model = FBFindModelByName("Cube") a = get_constraints_by_destination(model) print_name("destination:",a) model = FBFindModelByName("Cube") a = get_constraints_by_source(model) print_name("source:",a)
get_constraints_by_source(model) について
これは、引数に指定したモデルを参照先として使っているコンストレインを取得するファンクションです。
GetDst(DstIndex) によってモデルの Destination をチェックし、クラス名がコンストレインだったら取得といった流れです。
ちょっとややこしいですが、
コンストレインから見たモデルは Source であり、
モデルから見れば、コンストレインは Destination です。
get_constraints_by_destination(model) も同様です。
おしまい。
すごく地味ーなスクリプトばかり紹介してしまいましたが、役に立ちますよねきっと・・・。
今後もMotionBuilderに関するスクリプトを紹介していけたらと思います(汗)ではでは!
※免責事項※
本記事内で公開している全てのコードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用くださいませ。
[…] *モーションビルダースクリプトTips https://dftalk.jp/cp-bin/wordpress/?p=4700 […]
Posted at 2013.08.5 14:03 by デジタル・フロンティア-Digital Frontier | DF TALK | MotionBuilderでScript!!便利なコンストレインの活用術!!(初級編)
[…] また以前のDFTalk記事でのモーションビルダースクリプトTipsや 公式に用意されているサンプルスクリプト等も併用してもらえると出来ることの幅は増えると思います。 […]
Posted at 2014.05.26 12:01 by デジタル・フロンティア-Digital Frontier | DF TALK | MotionBuilderのPythonスクリプトを始めたい方へ