« BA Ess。 | トップページ | 取得 その1。 »

2010年10月 7日 (木)

スプライトな野郎どもを選択。

いや、とある知り合いの野郎が、スプライトシェーダを持っているオブジェクトを全部選択するスクリプトが欲しいとかなんとかほざいていたんでね、ちょっとやってみたというだけなんですがね。 思ったより手間がかかってしまった。マテリアル周りのスクリプティングはあんまりいじってないからなあ。。。。



var oAllItemsWithSpriteShaders = XSIFactory.CreateObject( "XSI.Collection" );//結果を入れる箱ヨーイ

var oMatLibs = ActiveProject.ActiveScene.MaterialLibraries; // シーンの中の全 MatLib 取得
for ( var i=0; i<oMatLibs.count; i++ )    //    全 MatLib をひとつずつループ
{

    var oMatLib = oMatLibs(i);
    var oMaterials = oMatLib.Items; //    MaterialLibrary.Items で返って来るのは Material オブジェクト(らしい)
    for ( var j=0; j<oMaterials.count; j++ ) // 全 Material をひとつずつループ
    {
        var oMat = oMaterials(j);
        oAllItemsWithSpriteShaders.AddItems( GetItemsWithSpriteShaders( oMat ) );  //    Function に飛ばしてブツ取得
    }
}

//    返ってきた野郎どもを選択しておしまい
SelectObj( oAllItemsWithSpriteShaders );



//  以下、Material オブジェクトを受け取ってそこから Sprite を探して見つけたらその Material を使用している野郎どもを返すファンクション。

function GetItemsWithSpriteShaders( oMat )
{
    var oShaders = oMat.GetAllShaders( ); //    その Material にぶら下がる全ての Shader を取得(接続されてないのも含む)

    var oItemsWithSpriteShders = XSIFactory.CreateObject( "XSI.Collection" ); //    結果を入れる箱ヨーイ
    oItemsWithSpriteShders.unique = true;
   
    for ( var i=0; i<oShaders.count; i++ ) // 全シェーダをひとつずつループ
    {
        var oShader = oShaders(i);
        var FuckinClassID = XSIUtils.DataRepository.GetIdentifier( oShader, siObjectCLSID ); // Shader の ClassID を取得
        if ( FuckinClassID == "{6BA13137-7FF5-4B27-AEF6-D280DB0B5043}" )    //    ClassID がこの象形文字だった場合、それは Sprite。
        {
            oItemsWithSpriteShders.AddItems( oMat.UsedBy  ); //    Sprite だったら、それを使ってる野郎( UsedBy )をぶち込む
        }
    }
   
    return oItemsWithSpriteShders; //    ぶち込まれた野郎どもを返す
}



JScript です、念のため。
一応、動いているように見えますがね。


実行すると、シーン中で Sprite Shader が含まれるマテリアルを持つオブジェクトが全部選択されます。 Cluster のマテリアルが該当した場合、Cluster も選択されます。 Group や Partition が該当した場合、Group や Partition 自身が選択されるのではなく、そこに所属するオブジェクトたちが選択されるようです。
もし Partition に左右されたくないなら、新規の Partition でも作って全部のオブジェクトが何のマテリアルもアサインされていない Partition に所属している状態で実行した方がいいかな。 Group や Layer はどうしようもないか。


改良あるいは仕様変更するとすれば、Group や Partition そのものも選択の対象に入れるとか、Group や Partition でマテリアルを上書きされている場合でもそのマテリアルは無視して所属するメンバがローカルに持っているマテリアルだけを対象にするだとか、Cluster の場合は親オブジェクトを選ぶようにするとか、そんなとこでしょうかね。 そのうち気が向いたらやってみようかな。たぶんやらないな。


アプローチはいくつでもあるとは思うのですが、今回はひとまず、

 1.シーン中の Material Library を全部取得

 2.それぞれの Material Library に所属する Material を全部取得(これでシーン中の全マテリアルが対象になる)

 3.それぞれの Material に所属する全 Shader を取得(これでシーン中の全シェーダが対象になる)

 4.全シェーダをひとつずつ、ClassID を使って Sprite かどうかを判別

 5.Sprite だったら、その Shader が所属する Material に対して UsedBy の呪文を唱えて、その Material を使用中のアイテムを取得

 6.そのアイテムを選択して終了


という流れにしてます。 ユーザが選んでいるオブジェクトの中から探すようなアプローチの方が簡単だったかも。


ちと発見だったのが、GetAllShaders メソッドですね。 Material オブジェクトを取得して、その Material オブジェクトに対して Material.GetAllShaders( ) と実行すると、そのマテリアルにぶら下がるシェーダ達、つまり RenderTree のノード達が全部イッキに取得できるんですね。 これは便利だ。 この GetAllShaders メソッド、7.0 以降で搭載されたようです。 よって上記のスクリプトは XSI 七ちゃん以降でしか動かないはずです。前からあった Material.Shaders プロパティで返って来るのは、直近で接続されているシェーダだけでしたからね。ツリー全体をたどって取得してくれるわけじゃなかったですからね。  なんだ、こんな便利な機能、もっと早く搭載しろよ&搭載したら大々的にアナウンスしろよ嘔吐デスクっていうかアビッドゴルァ。

Sprite シェーダかどうかの判別は、やはり ClassID に頼らねばならないか。シェーダの名前で検索しちゃうと、ユーザが名前変えちゃったらヒットしなくなりますからね。 にしてもあのヒエログリフのような ClassID はなんとかならんのか。他に方法ないかなあ。
ちなみにシェーダごとの(というか全てのオブジェクトごとの) ClassID は、SDK Explorer ( Ctrl + Shift + 4 )から調べられます。



ってことで、動いたかね?
これでいいかね?
ビールよこせゴルァ



.

|

« BA Ess。 | トップページ | 取得 その1。 »

コメント

おれもやってみました。pythonです。

oSdr = Application.FindObjects("","{6495C5C1-FD18-474E-9703-AEA66631F7A7}") #すべてのシェーダを取得
l = [s.Root.UsedBy.GetAsText() for s in oSdr if s.ProgID=="Softimage.sib_sprite.1.0"] #スプライトのみにフィルタリングし、使用しているオブジェクトたちを文字列のリストで取得
Application.Selection.SetAsText(",".join(l)) #返ってきたものを,で繋いでセレクションセット

ProgIDってのも意外と使えますよ。
シェーダにRootってのがあるのは初めて知りました。
後、繋がっていないのも取得してしまうのが、痛いところでしょうか。
GetShaderParameterTargets でフィルタリングしないとですね。
UsedByは便利と思ったんですが、Partitionやらをゲットするには、Ownersを使って引っ張ってくるってのがいいんですかね。

あと、junkiさんの書かれているJを試したんですが、ウチのマシンでは動かなかった…。
classIDでフィルタリング出来なくて失敗しました。
バージョンによって違うってことなんですかね?
ウチは、2011SP1 64で試してます。

投稿: garu | 2010年10月 7日 (木) 11時54分

ブログ見てビックリしました。
ありがとうございます。
ほんと頭が上がりません…(元から上がりませんが)
仕事で手いっぱいで勉強していませんでした。
作業効率が倍の早さになります…情けない…。
じっくり読んで理解します。

2010 64bitでは問題なく動いています。

ビール送るというかおごります。

投稿: maruyama | 2010年10月 7日 (木) 19時41分

ガルさん、 マジ? 3行でできちゃうの? なんか俺、ハズカシイw

ProgIDなんて知りませんでした。ほう、ClassID の象形文字よりずっと良いですな。今度調べて使ってみます。

Shader.Root も知りませんでしたー  ほうほう、シェーダからマテリアルやカメラやライトにたどり着けるんですね。便利ですね。ほうほう。
俺が書いたスクリプトは、先に Material オブジェクトを取得してそこから Shader を取得しているので、Sprite を発見した時点ですでに Material は取得できているわけで、この Root は必ずしも必要でない感じですが、garu さんのやつはいきなり最初から Shader を取得しているようなので、Root 必須ですね。

俺の書いたJが動かないそうですが、2011 からレンダリングまわりが変わったので、たぶん ClassID とか変わっちゃったと思います。どこかで読んだような気が。 2011 上で Sprite の ClassID を調べて(SDK Explorer か何かで)、書き換えて使ってみて下さい。 他の人の報告にあるように、2010 では動いているそうです。 俺は 7.01 上でやってました。

それと、こちらでは garu さんのスクリプトが動かないんですよ。エラーメッセージ忘れましたが、3行目で XSI 様が何かご不満を述べられておりました。7.5QFE 上でした。他の環境では試せていません。 Python のバージョンとかあるのかな? join とかが嫌われているということはないかしら?

Material オブジェクトから Partition オブジェクトを取得する場合は、はい、Owners の中身をループして、 7 以降なら type で partition だけフィルタリングできると思います。Owners.Filter( "Partiton" ) はなぜか効きません。なぜだ? ゴルァ






maruちゃん、
おお、そうかそうか。
はよビールよこせやドルァ

投稿: junki | 2010年10月 8日 (金) 02時32分

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: スプライトな野郎どもを選択。:

« BA Ess。 | トップページ | 取得 その1。 »