« 2011年12月 | トップページ | 2012年2月 »

2012年1月

2012年1月29日 (日)

マテリアルなグループでポン。

不真面目な元気な某氏はどうやら雪国で広い板の上に乗って斜面上を重力に引っ張ってもらう遊興に耽っているらしい。 真面目な不元気な俺は今日も仕事してるぜオルァ


そんな某氏をあざ笑うかのように、以前書いたスクリプトを投下してみるテスト。




JScript
---------------------------------------------------------------

var oGroups = XSIFactory.CreateObject( "XSI.Collection" );
for ( var i=0; i<Selection.count; i++ )
{
    if ( Selection(i).type == "material" )
    {
        var oMat = Selection(i);
        var oObjectsUsingTheMat = oMat.UsedBy;
        var oGroup = ActiveSceneRoot.AddGroup( oObjectsUsingTheMat, "Mat_" + oMat.name );
        oGroups.Add( oGroup );
    }
}


---------------------------------------------------------------


Explorer からマテリアルを選択して実行します。

すると、そのマテリアルを使っているオブジェクト&クラスタが入った Group ができます。 1マテリアルにつき、1グループですね。

以上。





俺もずっと前に書いていました。 誰でも考えることは同じというか、ええ、こういう需要ありますよね。 ならなぜ標準機能で搭載しないのだ嘔吐デスクゴルァ。




これだけではあまりにもアレなので、コメントを入れてみましょう。


JScript
---------------------------------------------------------------



//    選んでいるものをループするぜ
for ( var i=0; i<Selection.count; i++ )
{
   
//    もしタイプが Material だったら以下を実行するぜ
    if ( Selection(i).type == "material" )
    {
       
//    これは代入しただけだぜ。 別にしなくてもいいんだぜ。
        var oMat = Selection(i);
       
      
  //    Material.UsedBy で、そのマテリアルがアサインされているオブジェクト/クラスタがコレクションとしてゲットできるぜ
        var oObjectsUsingTheMat = oMat.UsedBy;
       
       
//    取得したコレクションをメンバにした Group をシーンルートに作るぜ
        var oGroup = ActiveSceneRoot.AddGroup( oObjectsUsingTheMat, "Mat_" + oMat.name );
       
      
//    作った Group は oGroups に追加しておくぜ
        oGroups.Add( oGroup );
    }
}

//    最後に選択しておしまいだぜ
SelectObj( oGroups );


---------------------------------------------------------------


ポイントは UsedBy でしょうかね。 これは Material オブジェクトに対して効くプロパティのひとつですね。 そのマテリアルを使っている全オブジェクトを返します。 

なので UsedBy を使うためにはまず Material オブジェクトが取得できてなくてはいけないのですが、この場合は、Selection から取得済みですね。 Selection(index) と書けばそれは既にオブジェクトになってますから。 でもそのオブジェクトが Material オブジェクトとは限らないので、if 分の中で type を調べて、 Material だった場合のみ処理をします。 なのでこの UsedBy を使う時点で既に Material オブジェクトを取得できているわけです。





UsedBy が返してくるオブジェクトは、3Dオブジェクトかもしれないし、グループかも知れないし、クラスタかもしれないですね。なんでもかんでも返します。 数は1つかも知れないし、10個かもしれない。 

返って来るのは Collection です。 UsedBy のヘルプを見れば、コレクションを返すと明記されています。 UsedBy の場合、正確には、ProjectItemCollection ですが、これはコレクションの中身の種類を表すものであり、今はどうでもいいです。  コレクションとは、「集合体」です。  この集合体ってのは、まあ、平屋1階建ての1人暮らし用アパートだと思えばいいと思います。 個室に分かれいていて、それぞれの部屋には1人しか入れません。 平屋と言ったのは、2階以上がないからです。水平に部屋の数が増えていく平屋アパートです。垂直には増えません。

コレクションとして返って来る場合は、1つしか見つからなくても、やはりコレクションとして返って来ます。


まあいいや、今回のスクリプトの中では、コレクションであるかどうかは、まあ、あまり本題とは関係ないですね。 ついでにコレクションの性質を述べてみただけです。





で、UsedBy を使って目的の人たちを取得できたら、そいつらを使って Group を作ります。これは AddGroup メソッドですね。 Model オブジェクトに対して効く呪文です。 なので AddGroup を使って Group を作ろうと思ったら、まずは Model オブジェクトを取得しなければいけません。 

でも今回の場合はシーンルートでいいので、ActiveSceneRoot.AddGroup としています。 ActiveSceneRoot ってのは、その中身にシーンルートがオブジェクトとして取得されているものです。 シーンルートってのは Model ですから、ActiveSceneRoot と書けばもうその中身は Model オブジェクトです。 なので、AddGroup の呪文が効きます。 

「AddGroup を使うために、俺は今、Model オブジェクトを取得しているんだぜ」 ということを明確に意識したい場合は、

 var oModel = ActiveSceneRoot;
 oModel.AddGroup( うんたらかんたら );


と書いてもいいですね。まあ、冗長ですけど。
冗長でも、初心者のうちは、このように書くほうを俺はオススメしたい気分。 自分が何をしているのか明確に意識できますからね。 理解への近道です。








上のスクリプトの場合、Material を選んでいることが前提になってます。 これを、他のものが選ばれていても動くようなスクリプトに改造します。



JScript
---------------------------------------------------------------

//    処理対象のマテリアルをまず全部引っかき集めるぜ。器はこれな。 
var oTargetMaterials = XSIFactory.CreateObject( "XSI.Collection" );


//    器に入る法則はこれな。
//        MatLib を選んでいた場合 → その MatLib 以下のマテリアル全部が対象
//        マテリアルを選んでいた場合 → そのまんま、そのマテリアルが対象
//        何も選んでなかった場合 → シーン全体のマテリアルが対象
//        MatLib か Material 以外のものが選ばれていたら → 何もしない

//    行くぜ

//    何も選んでなかったら

if ( Selection.count == 0 )
{

    //    シーンにある全部の MatLib をかき集めるぜ
    var oMatLibs = ActiveProject.ActiveScene.MaterialLibraries;
//コレクションとして帰ってくるから、変数名は複数形にしようぜ
   

    //    かき集めた MatLib をループして、そのLib以下にあるマテリアル全部 = Lib.Items を器に入れるぜ
    for ( var i=0; i<oMatLibs.count; i++ )
    {
        oTargetMaterials.AddItems( oMatLibs(i).Items );
    }
}


//    何も選んでない場合は上の if 以下が実行され、そしてここ以下の for ループは発生しないから、
//    ここで上の if に対して else はしないぜ。 で合ってますかどうですか。

//    選んでいるブツをループ

for ( var i=0; i<Selection.count; i++ )
{

    //    もし MatLib だったら、Lib 以下にあるマテリアル全部 = Lib.Items を器に入れるぜ
    if ( Selection(i).IsClassOf( siMaterialLibraryID ) )
    {
        oTargetMaterials.AddItems( Selection(i).Items );
    }
   

    //    もし Material そのものだったら、そのまま器に入れるぜ
    else if ( Selection(i).IsClassOf( siMaterialID ) )
    {
        oTargetMaterials.Add( Selection(i) );
    }
   

    //    上の2つに引っかからなかったら、違うものを選んでいるということなので、器に追加しない
}




//    後で Group を選択したいので、作った Group を入れる箱を用意しておくぜ
var oGroups = XSIFactory.CreateObject( "XSI.Collection" );

//    器に入ったマテリアルをループするぜ。 器が空っぽなら、このループは発生しないぜ

for ( var i=0; i<oTargetMaterials.count; i++ )
{
    var oMat = oTargetMaterials(i);
   

    //    Material.UsedBy で、そのマテリアルがアサインされているオブジェクト/クラスタがコレクションとしてゲットできるぜ
   var oObjectsUsingTheMat = oMat.UsedBy;
   

    //    取得したコレクションをメンバにした Group をシーンルートに作るぜ
    var oGroup = ActiveSceneRoot.AddGroup( oObjectsUsingTheMat, "Mat_" + oMat.name );
   

    //    作った Group は oGroups に追加しておくぜ
    oGroups.Add( oGroup );
}

//    最後に選択しておしまいだぜ
SelectObj( oGroups );


---------------------------------------------------------------



最初のスクリプトでは、選択中のブツをループして、1個1個 Material なのかどうかを調べてから処理をしてました。 

改造したスクリプトでは、ユーザの選択状態から判断して、まずは処理対象になるマテリアルを全部集めてしまっています。 そして、結果として集まったマテリアルをループして、実際の処理(Group 作成)を行っています。




MaterialLibraries は、Scene オブジェクトに対して効くプロパティです。 そのシーンに属する全ての Material Library を、コレクションとして返します。

なので MaterialLibraries を使いたければまずは Scene オブジェクトを取得せねばなりません。 現在開いているシーンのことです。 が、そんなもん、ActiveProject.ActiveScene と覚えてしまえばよろしい。



MaterialLibraries を取得したら、これはコレクションですから、中身=つまり個室に入っているひとりひとりの住人=この場合は Material Library ひとつひとつについて処理を施します。 なので MaterialLibraries をループするのです。

ループして、住人の1人= Material Library ひとつに目を付けたら、Items プロパティを使います。 これまた、コレクションを返すプロパティです。 結果は、Material コレクションです。 その MatLib に所属する全部のマテリアルが、それぞれ個室に入ったアパートを返してくるわけです。 



これを、自作アパートである oTargetMaterials の個室にブチ込んでいくのが AddAddItems です。 単数のブツを放り込むときは Add で、複数を放り込むのが AddItems ですが、よくわかんなかったら AddItems を使うのが良いでしょう。 AddItems は Add を兼ねていますから。  ちなみに AddItems を使って複数のブツをコレクションに放り込んだ時でも、1つの部屋に複数のものが入ったりはしません。2号室3号室4号室にひとりずつブチ込むという行為をいっぺんにやるというだけであり、2号室に3人入るわけではないです。


こうして、結果、選んでいた MatLib や、あるいはシーン全体の MatLib からかき集められた Material オブジェクトが個室に一人ずつ入った状態のアパート = oTargetMaterials コレクションが出来上がります。

あとは、このアパートの住人= Material それぞれについて、UsedBy を使って使用中の連中を Group にブチ込んでいくだけです。その辺の処理は、最初のスクリプトと全く同じなので解説は上記を参照。



あ、あと、この改造バージョンの方では、選んでいるブツが Material かどうかを調べるのに、type を使わずに ClassID を使っていますね。 xxx.IsClassOf( siMaterialID ) の部分です。 これは、最初のスクリプトの xxx.type == "material" って書いた部分と全く同じです。 どっちでもいい。



そのマテリアルを使っているブツが無かったらグループを作らない、という処理を入れてもいいかもしれません。 上のスクリプトだと、そのマテリアルを使っているブツが無かった場合は、空っぽのグループを作ります。  まあ、これはこれで、あ、このマテリアルは不要だな、とか判断できていいような気もする。








どうでしょうかね。
雪山から戻ってきたばかりだと、イミフでしょうかね。


っていうか俺も雪山に連れてって下さいよ。
2本の板に乗って重力に引っ張られる方は、好きなんです。
経験年数(30年以上)の割には上手くないけど、まあ、そこそこできますよ。
行きましょうよ。






.

| | コメント (1) | トラックバック (0)

2012年1月28日 (土)

愛しの chm (;´Д`)ハァハァ

久しぶりにプラグインページに追加。




愛しの chm (;´Д`)ハァハァ
http://homepage3.nifty.com/jjj/XSIFiles/Plugin/JJJ_XSI_Plugins.html





まあ、アレです。
chm です。





XSI 2012 から、マニュアルが HTML バージョンになりやがってねえ。 まあアレはまあアレでいいけど、chm バージョンも捨てがたいじゃないですか。あのサクサクの軽さ。快適なインクリメンタルサーチ。 特に次々にリンクをたどりながら調べていくオブジェクトモデルのスクリプティングでは、この快適なナビゲーションは捨てがたいです。


なのでまあ、chm バージョンのマニュアルもサクっと表示できるようにしておきましょうと、それだけのプラグインです。 HTML バージョンの代わりになるものではありません。旧バージョンに含まれる chm ガイドを、単体で起動できるようにしているだけです。 しかも OS のインターフェース上から起動しても全然いいわけなので、デスクトップにショートカットでも置いておけばその操作が死ぬほど面倒ということもありません。 とは言え XSI の中からも簡単に起動できた方が便利かなー、って思っただけですよ。




ちなみにユーザガイドだけは最新の 2012 の chm バージョンがありますので、それをダウンロードしておけばいいと思います。  もちろん HTML バージョンの代わりとして指定できるわけではなく、あくまでも単体起動して使うものですけどね。  このプラグインでユーザガイドの部分は、この 2012 chm バージョンを指定しておけばいいと思います。



ほんとは SDKガイドの方こそ、chm バージョンが欲しいんだけどねえ。



参考リンク:
http://xsisupport.wordpress.com/2011/04/13/chm-version-of-softimage-2012-user-guide/


.

| | コメント (0) | トラックバック (0)

2012年1月24日 (火)

NewSceneOptions。

そうそう、最近また使い始めたんですが、便利なんですよこれが。



NewScene Options

http://www.xsibase.com/tools/plugins.php?detail=1358


まだ生きていたのか、XSI Base のダウンロードセクション。



よくある、スタートアップイベントってやつですね。 XSI が起動した時、New Scene を実行した時に、自動で何かをさせるものです。 どっかに保存しておいた自分の「スタートシーン」を指定しておくとそれが勝手にロードされますし、スクリプトファイルを指定しておけばそれが実行されます。 基本はこの2つの使い方しかないと思います。 俺の場合はスクリプトを指定しています。



すんげえ古いプラグインですが、昔使っていたのにしばらく使ってなくて、最近思い出して上記のページから DL して 2012 SP1 にブッ込んだら、問題なく動いてますね。 Softimage の中の人で、元コメディアンという意味の分からない経歴を持つ、我らがチニー大先生が作ったプラグインです。





XSI を起動した後、または新規シーンを出した後に毎回必ず儀式のようにやる作業があるのなら、これを使うと便利じゃないですかね。 


俺の場合、大昔に撲滅アンビエンスというプラグインを作ったことがあるんですが、これは XSI デフォルトの 0.2 のシーンアンビエンスを嫌って、毎回手でゼロに設定していたので、ある日とうとうブチ切れて書いたものでしたが、しくみはこの NewSceneOptions とほぼ同じものです。 今回これを使うことにしたので、自動で実行させるスクリプトファイルの中に撲滅アンビエンス相当のことを書いてしまい、撲滅アンビエンスはアンインストールしてしまいました。 撲滅アンビエンス君、きっと4年くらい俺の XSI に常駐して、来る日も来る日も新規シーンのアンビエンスを撲滅し続けてくれたよね。俺は本当に感謝している。 いつかこの日が来るのは分かっていた。立派に役割を果たして去り行く君を、笑って送り出そう。 Rest in peace。


アンビエンス以外にも、俺の場合は、例えば選択中のブツのポイントを強調表示させたかったり、HiddenLine のカラーはオブジェクトのディフューズ色じゃないと嫌だったり、Camera ではなく Camera Root が 0, 2, 20 とかに居るのが激しく気に入らないとか色々あるので、ひと通りスクリプトに書いて、気に入った状態で始められるようにしてます。



俺のスクリプトファイルはこんな感じ。


JScript
------------------------------------------------------------------------

//    Ambience
var oAmbientLighting = Dictionary.GetObject( "Scene_Root.AmbientLighting" );
oAmbientLighting.ambience.red.value = 0;
oAmbientLighting.ambience.green.value = 0;
oAmbientLighting.ambience.blue.value = 0;


//    Camera
var oCam = ActiveSceneRoot.FindChild2( "Camera", siCameraPrimType );
var oRoot = oCam.Parent;
var oInterest = oCam.Interest;

oRoot.posx = 0;
oRoot.posy = 0;
oRoot.posz = 0;
oInterest.posx = 0;
oInterest.posy = 0;
oInterest.posz = 0;
oCam.posx.value = 0;
oCam.posy.value = 2;
oCam.posz.value = 20;

oRoot.Properties( "Visibility" ).Viewvis.value = true;
oRoot.Properties( "Visibility" ).Rendvis.value = true;
oInterest.Properties( "Visibility" ).Viewvis.value = true;
oInterest.Properties( "Visibility" ).Rendvis.value = true;
oCam.Properties( "Visibility" ).Viewvis.value = true;
oCam.Properties( "Visibility" ).Rendvis.value = true;

oCam.name = "Camera0";
oRoot.name = "Root0";
oInterest.name = "Interest0";



//    Light
var oLight = ActiveSceneRoot.FindChild2( "light", siLightPrimType );
oLight.Properties( "Visibility" ).Viewvis.value = true;
oLight.Properties( "Visibility" ).Rendvis.value = true;
oLight.name = "light0";



//    Viewport
//    Selected Points / Cones

SetValue("*.camvis.compobjselvert", true, null);
SetValue("Views.View*.*.camvis.compobjselvert", true, null);

SetValue("*.camvis.attrselcones", true, null);
SetValue("Views.View*.*.camvis.attrselcones", true, null);

SetValue("*.camvis.attrunselcones", true, null);
SetValue("Views.View*.*.camvis.attrunselcones", true, null);



//    Light off
SetValue("*.camvis.objlights", false, null);
SetValue("Views.View*.*.camvis.objlights", false, null);


//    Camera off
SetValue("*.camvis.objcameras", false, null);
SetValue("Views.View*.*.camvis.objcameras", false, null);


//    HiddenLine Color
SetValue("*.camdisp.hidlincoltype", 1, null);
SetValue("Views.View*.*.camdisp.hidlincoltype", 1, null);


//    Wire on unselected
SetValue("*.camdisp.wireontopunsel", true, null);
SetValue("Views.View*.*.camdisp.wireontopunsel", true, null);


Logmessage( "Junki NewScene Script ver120109  (゚∀゚)" );


------------------------------------------------------------------------


新規シーンの状態で実行されるわけだから安心して名前でハードコーディングしてますし、1回手でやってみた後にスクリプトエディタからコピペしただけのコードの方が多いですね。 でもたったこんだけで、ずいぶん便利です。  いずれもっとでかいスクリプトになっていくのかな。



起動時に、例えばサーバ上にあるワークグループディレクトリをまるごとローカルにコピーした上でそこをワークグループディレクトリに指定するとか、そういうのを自動でやっている人もいるようですね。 大規模なスタジオとかで、サーバ上にあるワークグループのツールを上書きアップデートする時とかに便利でしょうね。 あるいは、サーバ上のデータに常にアクセスに行くのは遅いので、起動の時にローカルにコピーする時間だけ我慢して以後は快適に使おうという用途でやっている人もいるようですね。













曲行きましょう。



最近気に入っているんですよこの人たち。

って言ってもラジオではこの曲しかかからないですけどね。 まだデビュー間も無いみたいだな。


歌詞が分かればもっと楽しいだろうにという思いと、歌詞を知ってしまうとがっかりしそうだから曲とその雰囲気だけ楽しめればいいやという思いと、いつも両方ありますね。 この曲なんかはもしかして、いかにもアメリカのカントリー音楽ファンに多そうな庶民的なキリスト教らしい日常に基づいているとか、そんなのかなあ、と想像したりするんだけどまあ、あえて調べず。  でもそのうち、サビだけでも口ずさめるようになりたくなったりして、調べちゃうんだよなあ。。。。。





.

| | コメント (0) | トラックバック (0)

2012年1月23日 (月)

取得 その7。 シーンのファイル名の取得。

某飲み会で某くんと話をしていて出た話題。


シーンのファイル名って取得できるんスか? と。
もちろん出来ます。
色んなスクリプトで使ってます。



var oProject = ActiveProject;
var oScene = oProject.ActiveScene;


Logmessage( oScene.Parameters( "filename" ).value );

Logmessage( oScene.filename.value );

Logmessage( ActiveProject.ActiveScene.filename.value );


Logmessage の3行は、どれも結果は同じです。


XSI のシーンというものに対して何かをしたい場合、この場合は「名前を調べたい」なわけですが、基本的にシーンオブジェクトというものを取得しなければいけないと考えればいいんじゃないですかね。 シーンオブジェクトさえ取得できれば、そのオブジェクトが、「私は HDD上のどこにどんな名前でセーブされているの?」 という情報を持っています。


で、シーンオブジェクトを取得するためには、先にアクティププロジェクトオブジェクトも取得しなければいけないんです。



と言うよりは、ActiveProject オブジェクトが持つプロパティのひとつに、ActiveScene というものがあり、これがシーンオブジェクトそのものだ、という方が正確な説明になるかと思います。



まあ、シーンオブジェクトなんていう基本的なものは、成り立ちなんてどうでもいいから、こうすりゃ取得できると覚えてしまえばよい。 それが、最初の2行です。 これで変数 oScene にシーンオブジェクトが取得できました。 



あとはマニュアルで Scene オブジェクトのページを見てみると、 FileName パラメータでフルパスが取得できると書いてあります。 なので Logmessage でそのパラメータの値を表示させています。


最初の

 Logmessage( oScene.Parameters( "filename" ).value );

これは、「 oScene が持つパラメータのうち、"filename" という名前のものだけを抽出し、その値( value )を表示せよ 」 と言っているわけですね。



でも Parameters( "ほにゃらら" ) ってのは、よく省略できます。 なので省略した書き方が、次に出てくる

 Logmessage( oScene.filename.value );

です。 結果は全く同じです。



いちいち変数に代入しなくても、直接アクセスもできまっせという例が、その次の、

 Logmessage( ActiveProject.ActiveScene.filename.value );

です。 この行は、最初の2行で書いた oActiveProject や oScene を使っていません。 一度も変数に代入してないわけです。 よってこの1行だけでも動きます。 

ファイル名を取得したいだけで他のパラメータにアクセスする必要はないだとか、スクリプトの中で何度も出てくるわけではない場合なら、このように、変数には代入せずに直接アクセスで書いてしまった方がコードは短くなるし、スッキリします。

でも俺なんかは、○○を取得してるんだぜっていう意識を明確にするために、どの時点でどこまでを取得できているのかを明確にするために、あるいは、冗長でもいいから後で見直した時に把握しやすいようにという意味で、わざといちいち変数に代入することもあります。 ケースバイケースですね。



まあこうしてごちゃごちゃ説明を書いてはいますが、「シーンのファイル名って、スクリプトで取得できるのん?」 などという段階の人、つまりスクリプティングにさほど慣れているわけではない段階の人であれば、めんどくさいこと考えずに、こう書いて実行させれば現在開いているシーンのファイル名がフルパスで表示されるって覚えちゃえばいいですよね。 理屈は後から着いて来ますから。







ごきげんよう。

| | コメント (0) | トラックバック (0)

2012年1月20日 (金)

GridData に色つけてポン。

唐突に。




JScript
-----------------------------------------------------------------

var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "ガッデムGridData";

var oGP = oP.AddGridParameter( "Grid" );
var oGrid = oGP.value;

oGrid.RowCount    = 2
oGrid.ColumnCount = 2 ;

oGrid.SetRowLabel( 0, "職場で" );
oGrid.SetRowLabel( 1, "全裸で" );
oGrid.SetColumnLabel( 0, "正座して" );
oGrid.SetColumnLabel( 1, "待機中" );

oGrid.SetColumnType( 0, siColumnBool ) ;
oGrid.SetColumnType( 1, siColumnBool ) ;

oGrid.SetCell( 0, 0, 1 );
oGrid.SetCell( 0, 1, 1 );
oGrid.SetCell( 1, 0, 1 );
oGrid.SetCell( 0, 0, 0 );

//    どの Row でもいいから一度 GetRowBackgroundColor を使ってカラーオブジェクトを取得
//    おかしな話だが、ゼロからカラーオブジェクトを作る方法が無いらしい

var oColor = oGrid.GetRowBackgroundColor(0);

//    取得したカラーオブジェクトの RGB に、0-255 で値をセット
oColor.Red = 222;
oColor.Green = 0;
oColor.Blue = 0;


//    そのカラーオブジェクトを Row にセット

oGrid.SetRowBackgroundColor( 0, oColor );

//    カラーオブジェクトはもう手中にあるので、もう1回取得する必要はなく、再利用する。
oColor.Red = 0;
oColor.Green = 200;
oColor.Blue = 0;
oGrid.SetRowBackgroundColor( 1, oColor );


var oL = oP.PPGLayout;
oL.AddItem( "Grid", siControlGrid );


InspectObj( oP );

-----------------------------------------------------------------

Grid



Grid Data に色を付ける方法。



Color オブジェクト
というもの = RGB の入れ物 が必要なのだが、ゼロから作る方法が用意されていない。

なので、Row から GetRowBackgroundColor を使って取得してくることで、入れ物を入手する。


一度入れ物が手に入っちまえばこっちのもん。あとは RGB を指定して、セットしてやるだけ。


Color オブジェクトをゼロから作る方法が用意されてないというあたりが、阿呆な仕様に見えます。Softimage さん?



これしか方法を知りません。
以前エアコン屋さんに聞いた方法です。







ごきげんよう。

.

| | コメント (0) | トラックバック (0)

2012年1月19日 (木)

悲しみの対称複製そしてリネーム。

以前、対象複製同時名前健作痴漢。などという記事を書いたのですが・・・。


シンメトリ複製する時に、名前の LeftRight にするみたいに、左右のリネームを同時にやろうというものでした。 後からリネームするのって麺独裁じゃないですか。 名前の最後が数字で終わってるオブジェクトだと数字が加算されてうざったいし。 なのでそういうことを自動化するスクリプトを書いたのでした。


その後PPG付きにしたのでオプションもその都度変えられるようになり、それなりに便利に使っていたんですよ。




でも、今日使っていて、思いっきり不具合に出くわしました。


1つのオブジェクト、1つのブランチとかでやる場合には問題なかったのかな。 よくわかんないけど、今日の作業では全然ダメでした。 それなりに複雑なリグのツリーを対称複製しようとしているのですが、ボーン階層とコントローラ階層の関係のように、コンストレインやエクスプレッションの関係も一緒に対称複製したいので、複数のツリーをブランチ選択してイッキに対称複製する必要がありました。 1つ1つ対称複製していたんじゃ、コンストの関係が切れるというか、複製元へのコンストのままになってたりしますからね。


でも、やってみたら、もう、オブジェクトの名前がぐちゃぐちゃにシャッフルされてしまっていました。 対称複製自体は正常にできているし、コンストもエクスプレッションの関係もちゃんと複製されている。 でも、名前がズレている。  A という親から B と C に枝分かれするツリーで、LR の名前置換自体はちゃんとされているけど、LR 以外の部分で B と C の名前がテレコになったりしてました。 あかん。 これではあかん。 何がなんやら分かんなくなってしまう。




前のスクリプトが前提にしていたのは、 「複製されたツリーは、複製元のツリーと親子の順番が一緒である。 だから、元が5番目の子は複製後のツリーでも5番目の子であり、インデックスの一致を信じてリネームができる」 ということでした。 


でもどうやら、そうとも限らない気がして来ました。 なんかもう、結果の名前がぐちゃぐちゃでしたからね。 で、自分で書いたコード読み返してみても、なんだこりゃって気もしました。 すげえテキトーに見えました。 最近ちょっと弱っているのもあって、すごく悲しくなりました。 対称複製ごときで悲しむなんて俺の人生終わっています。 



ということで、前回の時点で検証不足だったのはもちろんですが、今回もまた、どこがどう不具合なのかを深く検証することはしていません。 しかし、ともかく、ほんとに信頼できんのか怪しい上記の前提条件はポイと捨てて、もっと確実な方法でリネームするよう改造した方が良かろうと思いました。 なにせ今作っているリグが(それほどでもないけど)それなりに複雑になってきたので、もし不具合があったら後から直すのが大変なわけですよ。 なのでアルゴリズム的にもっと確実にするというかシンプルにするというかね、ここは原理を見直した方が良かろうと思ったわけですよ。







ってことでなんとか改造したバージョンを、一応載せておきますね。


っていってもコードが少し長いし、セルフインストール型にしてしまっているので、ソースを直接載せるのではなく、ファイルのダウンロードリンクを貼っておきます。


 悲しみの対称複製そしてリネーム。

 ダウンロード KanashiminoSymDupAndRename.js (13.4K)





自分のプラグインフォルダに入れて下さい。 XSI を再起動するかプラグインをリロードすると、XSI 右側の Edit メニューの中に出現します。 


2



あとは、普通に対称複製したいブツをブランチ選択などで選んで(複数可)、このメニューから実行です。



するとPPGが出てきます。

Kanashimino


下段中央の部分は、標準機能の Duplicate Symmetry を実行した時と基本的に同じです。 

上の部分がオブジェクト名の検索&置換ですね。  性器正規表現も使えます。たぶん。 

このように、標準機能に検索&置換を付け加えたというだけのものです。










一応、新しい原理を説明すると・・・・


以前のようにインデックスに頼るのではなく、 「複製後に名前が必ず一定のパターンになるよう、複製元の名前をあらかじめ細工をする。 一定のパターンであるということが分かっているのだから、名前は安全に加工し直すことができる」 というものにしてあります。




例えば、元のオブジェクトが L_bone1 だったとしたら、

複製前に、L_bone1_FuckMePlease という名前にリネームしてしまいます。


そしてこの L_bone1_FuckMePlease を対称複製しますが、複製された時点では名前は必ず
L_bone1_FuckMePlease1 になります。 元の名前 + 1 ですね。  XSI ではいつもこうなりますよね。


そして、L_R_ に変換する。 すると、R_bone1_FuckMePlease1 になりますね。


そして 、R_bone1_FuckMePlease1 から _FuckMePlease1 を取っ払えば、めでたく R_bone1 という名前になります。 必ずケツに _FuckMePlease1 が付いているとわかっているからできる技です。 ここで複製後のオブジェクトのリネームは完了。


さらに、元のオブジェクトも L_bone1_FuckMePlease にリネームしちゃってますから、 _FuckMePlease を削除して、元の L_bone1 に戻してあげます。 これで全部おしまい。



という風に、複製前に、絶対にカブらなさそうな名前にいったんリネームして、しかも数字で終わらない文字列を付け加えることによって、複製後には必ず その名前+1 というパターンになっているようにしたということです。 たぶんこっちの方が確実だと思う。 

もし L_bone1_FuckMePlease という名前のオブジェクトが既に存在していたらこの方法はアウトなわけですが、そんな変態な名前を付けるのは俺とあの人とあの人くらいしかいないと思うので、まずカブらないでしょう。 ちなみに FuckMePlease の部分は実際はもうちょっと長い文字列にしてあります。  







一応、俺のもとではちゃんと動いているように見えます。 が、最終的に元に元に戻すとは言え、複製元のオブジェクトまでリネームする工程があるので、何か予期せぬ不具合が出ないかとか、なんとなく心配になりますよね。 エクスプレッションが入っていた場合、リネームされた時に正しく関係がアップデートされんのか? とか。  

でも今のところ、ちゃんと動いているように見えます。 それなりに複雑なリグでも試してみたんですが大丈夫に見えます。



どうでしょうかね。
どなたか試してみてくれませんかね。

なるべく複雑な条件で試してもらえるとありがたいですね。 エクスプレッションとかコンストレインとか、そういう関係を保ったまま複製されないと意味無いですからね。





UI 部分はまだ改良できそうですね。 検索&置換の部分をもっと入力しやすくするとか、前回入力した文字を覚えておくとか、プリセットを呼び出せるようにするとか・・・。 いずれ改造するかも知れません。





その前に、そもそもちゃんと動くか、試して教えてくれたらありがたいです(゚∀゚)





.

| | コメント (0) | トラックバック (0)

2012年1月18日 (水)

キーフレーム殲滅でポン。

昼間っから唐突に降ってくるスクリプト。

実は昨日の深夜に1時間くらいで書いて、さっき30分くらい手直し。 きっと俺の作業にも役立つ。



すいません、まだ解説無し。
いずれ。

動きますかね初夏さん。



JScript
-------------------------------------------------------

var oPC = Dictionary.GetObject( "PlayControl" );

DefMode = GetGlobal( "KeyFrameFucker_Mode" );
DefStart = GetGlobal( "KeyFrameFucker_StartFrame" );
DefEnd = GetGlobal( "KeyFrameFucker_EndFrame" );
DefLock = GetGlobal( "KeyFrameFucker_Lock" );

if ( DefMode == null )
{
    DefMode = 0;
    DefStart = oPC.In.value;
    DefEnd = oPC.Out.value;
    DefLock = false;
}

var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "キーフレーム殲滅でポン。";
oP.AddParameter2( "iMode", siInt4, DefMode );
oP.AddParameter2( "iStartFrame", siInt4, DefStart );
oP.AddParameter2( "iEndFrame", siInt4, DefEnd );
oP.AddParameter2( "bOverrideLock", siBool, DefLock );

var oL, oItem;
oL = oP.PPGLayout;
oL.AddRow();
    oL.AddGroup( "", false, 23 );
        oItem = oL.AddButton( "DeleteKey", "∀゜)⊃" );
        oItem.SetAttribute( siUICX, 0 );
        oItem.SetAttribute( siUICY, 110 );
    oL.EndGroup();
    oL.AddGroup( "", true, 54);
        oItem = oL.AddEnumControl( "iMode", Array( "Keep this Range", 0, "Fuck this Range", 1 ), "Mode", siControlCombo);
        oItem.SetAttribute( siUINoLabel, true );
        oItem = oL.AddItem( "iStartFrame", "Start" );
        oItem = oL.AddItem( "iEndFrame", "End" );
        oItem = oL.AddItem( "bOverrideLock", "Fuck Key Lock" );
    oL.EndGroup();
    oL.AddGroup( "", false, 23 );
        oItem = oL.AddButton( "DeleteKey", "⊂(゜∀゜" );
        oItem.SetAttribute( siUICX, 0 );
        oItem.SetAttribute( siUICY, 110 );
    oL.EndGroup();
oL.EndRow();

oL.Language = "JScript";
oL.Logic = DeleteKey_OnClicked.toString();

function DeleteKey_OnClicked()
{
    var oCol = XSIFactory.CreateObject( 'XSI.Collection' );
    oCol.items = Selection;
    var oExpanded = oCol.expand( );

    var oAllFuckinAnimatedParametersGodDamnItYouMotherFuckerEatThisShitSonOfABitch = XSIFactory.CreateObject( 'XSI.Collection' );
    for ( var i=0; i<oExpanded.count; i++ )
    {
        oAllFuckinAnimatedParametersGodDamnItYouMotherFuckerEatThisShitSonOfABitch.AddItems( oExpanded(i).NodeAnimatedParameters( siFCurveSource ) );
    }

    Mode = PPG.iMode.value;
    FrameStart = PPG.iStartFrame.value;
    FrameEnd = PPG.iEndFrame.value;
    LockOverride = PPG.bOverrideLock.value;   

    for ( var i=0; i<oAllFuckinAnimatedParametersGodDamnItYouMotherFuckerEatThisShitSonOfABitch.count; i++ )
    {
        var oFcurve = oAllFuckinAnimatedParametersGodDamnItYouMotherFuckerEatThisShitSonOfABitch(i).Source;
        if ( Mode == 0 )
        {
            oFcurve.BeginEdit();
                oFcurve.RemoveKeys( FrameStart - 9999999, FrameStart - 0.01, LockOverride );
                oFcurve.RemoveKeys( FrameEnd + 0.01, FrameEnd + 9999999, LockOverride );       
            oFcurve.EndEdit();
        }
        else
        {
            oFcurve.BeginEdit();
                oFcurve.RemoveKeys( FrameStart - 0.01, FrameEnd + 0.01, LockOverride );
            oFcurve.EndEdit();
        }
    }

    SetGlobal( "KeyFrameFucker_Mode", Mode );
    SetGlobal( "KeyFrameFucker_StartFrame", FrameStart );
    SetGlobal( "KeyFrameFucker_EndFrame", FrameEnd );
    SetGlobal( "KeyFrameFucker_Lock", LockOverride );

    Logmessage( "|∀゜)⊃ 終わりますた ⊂(゜∀゜|" );


    // 以下の1行追記。表示を強制アップデート。 2011までは多分必要。Thanks to 網ー誤マーティン
    SelectObj( oCol );
}

InspectObj( oP, null, null, siLock );

-------------------------------------------------------



Keyframefucker


Keep モードで、そのフレーム範囲内にあるキーフレームを保持しそれ以外を殲滅。 つまり指定したフレーム範囲より前にあるキーフレームと後にあるキーフレームが殲滅されます。 頭とケツ削除。

Fuck モードで、そのフレーム範囲内にあるキーフレームを殲滅。

Fuck Key Lock は、ロックされてるキーフレームでも強引に犯っちまうかどうか。

ボタンは左右同じです。どっち押してもいいです。

複数オブジェクト可。 オブジェクトをブランチ選択してればその子供も含まれる。 Group を選択していればその Group 内の全てのオブジェクトが対称。 つまり Expand の魔法




解説はまた後ほど。たぶん。



ごきげんよう。


------------------------------------------
追記:

コメント欄にありますが、ヤラたんからのタレコミで、2011 まではどうやら表示のアップデートに問題があったようでした。 キーフレーム削除後、表示が更新されないという。 実際はちゃんと削除されているんだけど、画面上即座にそれが確認できないという状態ですね。

俺はもともと 2012 でこのスクリプトを書いたのですが、2012 ではその問題が出なかったんですよ。 でも 2011 SAP SP1 で試してみたら、まさに同じ問題に出くわしました。

他のお方はどうですか? 同じ問題が出るお方はいますか? バージョンは?

とりあえず、ヤラたんの実験結果に従い、上のコードの赤字の部分、1行追加しました。 おそらくこれで、キーフレーム削除後に表示が強制アップデートされると思います。 2012 よりも以前のバージョンでは必要と思われます。 2012 以降のバージョンでも、まあ害にはならないので、この1行があって良いでしょう。

マーティンヤラたんありがとう (゜∀゜

------------------------------------------

.

| | コメント (4) | トラックバック (0)

新プリミティブ降臨。

ツイッタでどなたかが流してくれた情報です。





なにやら新しいプリミティブがたくさん追加されるプラグインが。

http://www.boundingboxgames.com/tools/essgeo





スヴァらしい(゚∀゚)

何種類もの新しいポリゴンプリミティブが追加されます。
全て、パラメトリックに調整が可能です。 軽いし。




何に使うのか意味不明なやつも多いですが、一番気に入ったのは、です。 そう。 マルですよ。


なんと、このツールには、が含まれるのです。
夢の○です。


Maru1

すげえ使えますよこのプリミティブは。 なぜこれが最初から無いのだ阿呆タレXSIゴルァですよ。


↓ずいぶん前にも、涙ぐましい努力でしてたんですね。
http://junkithejunkie.cocolog-nifty.com/blog/2011/02/post-f4b4.html






惜しいのは、中心から放射状になった分割線を入れるオプションが無いことですね。 まあ、ポリゴンを Ctrl + D で複製して Collapse すればすぐできますけどね。

Maru2



これ以外にも、カドが丸まった四角形とかすんげえ使えそうです。
歯車も使えそうです。










ところで作者さんのページに書いてますけど、インストールしてすぐに使えない場合は、Visual C++ 2010 support libraries とかいうものが無いからかも知れないとのことです。 


で、俺の試した環境は Windows7 64bit + XSI 2012SP1 64bit なんですが、やはり、インストールしただけではメニューに現れませんでした。 XSI を再起動しても現れませんでした。 

なので作者さんのページからリンクが張られているマイクロソフトのページに行き、Visual C++ 2010 support libraries を落とし、インストールした後 XSI を再起動したら、やっとメニューに出現しました。 参考までに。









いやあ、それにしてもスヴァらしい。
こういうの待ってました。
実に使える。
カッコ良すぎる。
俺もこういうツールを作りたい。
せっかくツール作っても変態だとかなんだとかディスられてばかりですよ俺は。






プリミティブって、もっと増えてもいいですよね。 ただのモデルなら .emdl としてどっかに保存していたものを Import するだけでもいいですけど、やっぱりパラメトリックにいじりたいじゃないですか。分割数とか。


今時なら、ICE ということになるんでしょうかね。
俺はまださっぱりですが。
きっと ICE を使ってパラメトリックでダイナミックに調整可能なプリミティブ集というのが出てきそうな気がします。なんか意味不明の形というか、デフォーメーションのパターンのようなものは既にあった気がしますね。 L-System的な。


ICE/非ICE に関わらず他にもこういうプリミティブ集みたいなやつとか知っていたらどうか教えて下さい。プリミティブは多いほうがいいです。






このツールは、さっそく今やってるリグでコントローラにでも使わせてもらいましょうかね。 エッジ取り出せばカーブにもなるしね。







.

| | コメント (0) | トラックバック (0)

2012年1月17日 (火)

アップベクちゃんご主人様捜索。

こいつのアップベクタ先はどのオブジェクトだっけ? という話ですよ。



シーンが複雑になってきて、ボーンの Chain Up Vector 先のオブジェクトが行方不明ということはよくあります。 人から渡されたシーンならなおさらですね。 もちろんスケマティックビューで接続線の先を探すこともできますが、もしも接続先のスケマティックノードが畳まれている状態だったらその線は表示されませんし、畳まれていなかったとしても横に長~いスケマティックになっていたりすると線の接続先までたどり着くのは大変だし、そもそもスケマティックビューを使わないなどというふざけた若僧が多いわけです最近は。



そういうふざけた若僧を甘やかすつもりはまったくありませんが、ともかくもアップベクタ先を見つけるのがめんどくせえ。 そう思ってました。




もしもチェインアップベクタがいわゆるコンストレインの種類であれば、Constrain > Select Constraining Objects で一発で選択できるし、スクリプティング的にも Constraint オブジェクトの Constraining プロパティからたどり着けます。 でもチェインアップベクタって、コンストレインじゃないんですよね。オペレータです。



↓Explorer で見ても、歯車アイコンですよね。歯車アイコンはオペレータです。 ICEオペレータとかと同じ。

Upv1

Kinematic Joint プロパティの下に存在するオペレータです。



俺、このオペレータというやつをよく分かってなくて、今までスクリプト書くときもなんとなく踏み込まないでいた領域だったかも知れません。 でも今日、作業していたシーンが複雑になってきて、アップベクタ先がわかんなくなって大いに混乱し、最後にはモニタ2個を室伏のようにぶんぶん振り回して窓から放って世界新を出してしまったので、しかたなくスクリプトで出来ないか調べてみました。 なんか、以前にもこんなスクリプトを書いたような気がしないでもないんだが、その記憶も曖昧で。




で、やってみたら、そんなに難しくなかったように思えました。 できました。

いや、ほんとはちゃんと分かってないので、そんなこと言えないんですけど。 どうやらちゃんと動いているっぽいから、たぶん大丈夫だろう、くらいの状態です。






------------------------------------------------------------

//    アップベクちゃんご主人様捜索

//    先に Chain Up Vector Operaror を選択中のオブジェクトから全部かき集める
var oUpVectorOps = XSIFactory.CreateObject( "XSI.Collection" );
for ( var i=0; i<Selection.count; i++ )
{

    //    安直に GetObject で取得。 たぶん "XXX.joint.SkeletonUpVectorOp" しかあり得ないと思うから。たぶんね。
    //    false 付けておかないと、目的のものが見つからなかった場合にエラー吐いて止まるので、false 必須です
    var oSkeltonUpVectorOp = Dictionary.GetObject( Selection(i).fullname + ".joint.SkeletonUpVectorOp", false );
    if ( oSkeltonUpVectorOp )
    {
        oUpVectorOps.Add( oSkeltonUpVectorOp );
    }
}

//    かき集めたオペレータをループして、拘束先を取得
var oUpVectorMasterObjects = XSIFactory.CreateObject( "XSI.Collection" );
for ( var i=0; i<oUpVectorOps.count; i++ )
{
    var oOp = oUpVectorOps(i);

    //    UpVector の場合、InputPort は常に1つしかないと思う。たぶん。 なので InputPorts(0)で大丈夫だと思う。たぶん。
    //    InputPorts(0)で Port オブジェクトを取得し、Target2 メソッドで拘束先である Kinematics.Global が取得され、
    //    Parent3DObject プロパティでその3Dオブジェクトを取得できる。 たぶんですよたぶん。

    var oTargetObject = oOp.InputPorts(0).Target2.Parent3DObject;
    oUpVectorMasterObjects.Add( oTargetObject );

    //    情報表示
    Str = oOp.Parent3DObject.fullname + " ( " + oOp.Parent3DObject.type + " ) -- UPV --> ";
    Str += oTargetObject.fullname + " ( " + oTargetObject.type + " )";
    Logmessage( Str );
}

//    かき集めた拘束先3Dオブジェクトを選択しておしまい。
SelectObj( oUpVectorMasterObjects );

------------------------------------------------------------





オブジェクト(ボーン)を選択して実行します。 すると、アップベクタ先のオブジェクトが選択された状態になります。存在していれば。

チェインアップベクタ専用です。 それ以外のアップベクタって何種類かあるのかな、普通のコンストレインの中のタブにもアップベクタがあるしよくわからんけど、ともかく、ボーンに対して行う Chain Up Vector しか考慮に入れてません。




一応、よくわかってないなりに、スクリプトの説明をしておきますね。




まず、チェインアップベクタというものの正体は、SkeletonUpVectorOp という名前のオペレータであるという所から始まります。 チェインアップベクタを実行すると、ボーンの Kinematic Joint の下にこのオペレータが発生しますね。 はい、まずはオペレータであり、 Kinematic Joint 以下以外には存在しないという存在パターンがつかめました。 存在パターンが限定できるってのは良いことです。取得が楽だからです。

そして、リネームしようとしても出来ない。 これまた実に好都合です。 リネームできるかどうか試してみるクセを付けるといいですね。 この場合は、出来ませんでした。 つまり、Kinematic Joint 以下に SkeletonUpVectorOp という名前で存在すればそれはもうチェインアップベクタのオペレータを指しているということですから、取得するのは簡単です。 


ってことで安直に、


 var oSkeltonUpVectorOp = Dictionary.GetObject( Selection(i).fullname + ".joint.SkeletonUpVectorOp", false );


このように書けばいいことになります。たぶん。たぶんですよ。 取得したいブツの名前が分かっている時は Dictionary.GetObject の呪文が楽です。

ただし、false を入れるのを忘れてはいけません。 もしアップベクタが無いボーンを選んでいた場合や、ボーン以外の、つまりチェインアップベクタがそもそも存在できないオブジェクトを選んでいた場合は、もちろん何も取得できません。 そしてこの false を忘れると、何も取得できなかった時にエラーで止まってしまいます。 これはよろしくありません。 

false さえ付けていれば、何も取得できなくてもエラーが出ません。 代わりに null が入ります。 なので次の行で null かどうかを調べれば取得できたかどうかが分かります。


ってことで、

  if ( oSkeltonUpVectorOp )

こう書いていますが、


  if ( oSkeltonUpVectorOp != null )

こう書いているのと同じ意味です。 null じゃなければ、つまりアップベクタオペレータを(スクリプティング的な意味での)オブジェクトとして取得できていれば、次が実行されるわけです。




次にオペレータの接続先を探すわけですが、ここでやはり大きなヒントになったのは、SDK Explorer でした。 スクリプトで未知の領域に踏み込むときは、まずはなんでもいいから SDK Explorer を開いて端緒をつかみます。 意味わかんなくても端緒さえつかめれば、芋づる式に出来ちゃったりしますから。


ってことで、SkeltonUpVectorOp を SDK Explorer で調べてみると・・・・

Upv2

あっ

Hage 発見! (゚∀゚)


自分でアップベクタ先に指定したオブジェクト Hage が、ちゃんと出ているではありませんか。 まさにこいつを取得したいわけですよ。 アップベクタを仕込んだボーンから始めて、この Hage にたどり着きたいのです。


そして見てみると、InputPortsTarget という所に載っていますよね。 これこそが端緒です。 俺は InputPorts も Target も意味を知りませんでしたが、この InputPorts だか Tartget だかというキーワードさえ見つかれば、あとは SDK ガイドで該当ページやそこからリンクが張られている周辺のページをごちゃごちゃ見てみれば、だいたいなんとかなったり、ならなかったりします。 今回はなんとかなりました。 いつもこうやって調べています。



で、まだちゃんと理解はしてないですが、どうやらオペレータというものには InputPort というものがあるらしい。 オペレータにとってのインプット、つまり何らかの情報を受け取るポート=窓口なんでしょうね。 この場合はアップベクタですから、アップベクタとして指定したオブジェクトのポジション情報がオペレータの窓口 = InputPort から入って来ると考えることができると思います。たぶんね。

InputPort はオペレータによっては複数あるようです。 そりゃそうですよね。パッと思いつくのは、例えば Extrude Along Curve などはカーブ2つから作るわけですし、Merge なんかも2つ以上のオブジェクトから成り立つわけですよね。 

ってことで InputPorts というプロパティがあり、結果は Collection です。 コレクション、つまり複数です。 たとえインプットが1つしかないオペレータでも、取得できるのは Collection です。 なのでそのうちから、目的のポートを1個特定しないといけない。


しかし上の SDK Explorer の画像を見るとわかりますが、SkeltonUpVectorOp の場合は InputPort が1つしかないんですね。 ほうほう。 こりゃ楽でいいわ。 


ってことで、


  Operator.InputPorts(0)


と書けば、コレクションの中から、目的のポート1つをもう取得できたことになります。コレクションとして返って来てはいるものの、1つしかないともう分かっているんだから、最初の1個(0番目)を指定すればいいということです。



で、ポートオブジェクトが取得できればポートオブジェクトに対して効くメソッドが全部使えるようになってますから俺はもう自由です。大空に羽ばたきます。


ってことで、Target2 というメソッドを使ってポートの接続先を取得します。


  Operator.InputPorts(0).Target2



この Target2 というメソッドもよく知りませんでしたが、SDK Explorer で Target と書いてある部分に目的のオブジェクトの名前(Hage)が載っていたもんですから、 ははー どうやら Target という何かがあるらしいぞ  と想像がつくわけで、そこまで想像できればあとは SDKガイドでうろうろしてるうちにどうにかなりますから、大したことはありません。


ただし、こうしてたどり着いた Target は XXX.Kinematics.global というものでした。プロパティですね。 ヌルなどの3Dオブジェクトそのものではなく、3Dオブジェクトが持つプロパティです。 アップベクタオペレータにとってはそのブツの 「ポジション」 がターゲットになるわけであって、ポジション情報というのはこの Kinematics.Global に格納されているから、Target2 で取得できるのは Kinematics.Global オブジェクトである、という風に考えていいんじゃないかと思います。

ここでは Kinematics.Global オブジェクトが取得できているので、こいつに対して使えるメソッドやプロパティは全部使えるようになっているわけです。もう俺は自由です。大宇宙に飛び出します。 


ってことで、Parent3DObject プロパティを使います。


  Operator.InputPorts(0).Target2.Parent3DObject


こう書いて、ようやく3Dオブジェクトにたどり着きました。 




あとは、情報を表示したり、たどり着いた3Dオブジェクトをコレクションにブチ込んでおいてあとでまとめて選択する、という処理を書いているだけです。





ってことで、ほんとにこれでいいのかな。 アップベクタ先の取得の仕方として正しいかどうか、実は自信がないんですが、一応今のところ想定通りに動いているように見えますよ。




いろいろ不正確かも知れませんなあ。
オペレータ弱いです俺。 スクリプトオペレータもよくわかりません。


ツッコミお待ちしています。

ごきげんよう。



.

| | コメント (2) | トラックバック (0)

2012年1月16日 (月)

脱退グループ。

書きたてほやほや。




//////    脱退グループ

//    Group 入れる箱ヨーイ
var oGroups = XSIFactory.CreateObject( "XSI.Collection" );

//    現在選択中のブツをループ
for ( var i=0; i<Selection.count; i++ )
{
   
//選択中のブツの Owners をループして Group だったら箱にブチ込む
    var oOwners = Selection(i).Owners;
    for ( var j=0; j<oOwners.count; j++ )
    {
        if ( oOwners(j).type == siGroupType )
        {
            oGroups.Add(  oOwners(j) );
        }
    }
}

//    確認用
//Logmessage( "所属Group = " + oGroups );

//    見つかったグループを全てループし、選択中のブツを RemoveMember していく
for ( var i=0; i<oGroups.count; i++ )
{

 //    その Group に所属してないやつを含んでる状態で RemoveMember しても別に怒られないみた
    oGroups(i).RemoveMember( Selection );
}





オブジェクトを適当に選んで実行します。 するとそのオブジェクトは、所属していた全ての Group から脱退します。 つまりどの Group にも属さないオブジェクトになるということです。


もともと複製して作ったオブジェクトなどは特に、シーンを整理して行くうちに、ああめんどくせえ、何がどれだかわかんなくなった、いったん全部の Group から脱退してやるぜ、ということがあるんですが、所属している Group を調べるのもめんどくさいし、いちいち右クリックで Remove from Group とかやってらんねえ、とういのもあり、思い立って書いてみました。




そんだけっす。



スクリプティング的には、Group の取得に Owners からたどっていますよというだけで、まあ、特筆すべきことはないかもしれない。

選択したものを全部ループして1つずつグループを脱退させて行くのではなく、選択したものが所属しているグループを先に全部集めてしまい、集まったグループをループして、選択中のオブジェクトがそのグループに所属していようがいまいがともかく全員脱退しろと言っているという、そういう順番の処理になってますね。 こっちの方が処理が速い気がしてやっているのですが、あんまり根拠はありません。


選択中のブツの種類を判定する処理とか何もしてないので、変なもの選んで実行するとエラーで止まったりとかすると思います。






ではごきげんよう。

| | コメント (0) | トラックバック (0)

2012年1月14日 (土)

ctr_dist と this_model の和解。

先日の ctr_dist と this_model の軋轢についてなんですが、何人ものお方が実験に協力して下さいましてですね、ひとつ方向性が見えた気がしますので、いったんベイクしておきます。 





まず、現象として、


  ctr_dist の中で this_model が使えなかった


というのは、実験してくれた人のおそらく全員で再現しました。 少なくとも、勘違いやオペレーションミスではないということになると思います。



ただし。


ctr_dist の中の pos の部分の書き方を変えると、 ctr_dist の中でも this_model が使えるということがわかりました。

つまり、pos の部分が前回の書き方の場合だとNGだということです。そこさえ書き直せば使えます。




順を追って説明します。



前回の書き方は、

 ctr_dist( this_model.Man_A, this_model.Man_B )

こうでした。 Man_A, Man_B という風に、オブジェクトの名前で終わっていました。この書き方だと、this_model を使わなければ問題ないが、this_model を使い始めた瞬間にエクスプレッションがセットされないということがわかりました。



次に、

ctr_dist( this_model.Man_A., this_model.Man_B. )
ctr_dist( this_model.Man_A.kine.global.pos, this_model.Man_B.kine.global.pos )



この2つの書き方を試してみましたが、やはり同じく、エクスプレッションはセットされませんでした。

前者は、オブジェクトの名前の後ろにピリオドを置いたものです。 こう書けとマニュアルにも明記されています。 でも this_model があるとガン無視されました。

後者は、global.pos です。 タテとかヨコだけじゃなく、2点間の3D空間的な距離を求めるので、X や Y などの特定の軸を記述せずに pos で終わっています。 これも this_model が無い時には問題ないのですが、this_model が入るとガン無視です。




しかし。



ctr_dist( this_model.Man_A.kine.global.posx, this_model.Man_B.kine.global.posx )




このように、posx まで書くと、this_model があってもちゃんと効くようになりますた!! (゚∀゚)


pos でやめてはいけなかった。

posx まで書かなければいけなかった。

なぜ?

なぜかはわかりません。

観察から挙動がわかったというだけで、理由まではわりません。ともかくそうなんです。





しかしこれだと、posx と軸を限定して記述しちゃってるんだから、2点間の距離と言っても、X 軸方向の距離しか取得できてないんじゃないの、Y 方向とかに動かしたらタテの距離は考慮されないんじゃないの、という気がしますよね。


でも、やってみたら、posx と書いていても、X軸に限らず Y も Z も考慮した正しい2点間の距離を取得できていることがわかりました。

どうやって実験したかと言うと、カスタムパラメータを作り(別に必ずしもカスタムパラメータである必要はないのですが)、そちらには this_model を使わず、しかも posx ではなく pos で終わっているか、あるいは pos すら書かずに Man_A で終わっている書き方で ctr_dist を記述しました。 つまり、this_model を使ってないため何も問題なく動作することがあらかじめ分かっている書き方で2点間の距離を取得したのです。 そしてその結果は、posx まで記述した場合と完全に一致しました。







ということで、結論は、



ctr_dist の中でthis_model を使いたければ、

オブジェクト名 や
オブジェクト名+ピリオド や
オブジェクト名 .kine.global/local.pos で終わっていてはいけない。

オブジェクト名 .kine.global.local.posx まで記述しなければいけない

X とか書いてるけど、ちゃんと距離は XYZ を考慮して正しく取れるからね。





ということになると思います。







また、他の組み合わせでも実験してみたんですが、行けるものと行けないものがありました。



ctr_dist( this_model.Man_A.kine.global.posz, this_model.Man_B.kine.global.posy )

この例は Z と Y と書いていますが、問題ないです。 XYZ がどう混ざっても問題なさそうです。




ctr_dist( this_model.Man_A.kine.global.posz, Model.Man_B.kine.global.posy )

今度は、左側のみ this_model を使い、右は Model名で書いていますが、問題ないです。





ctr_dist( this_model.Man_A.kine.global.posz, Model.Man_B.kine.global.pos )

さらに右側のみ posy の Y を削除して pos で終わっている形にしましたが、これも問題ないです。 pos で終わっているとダメなのは this_model を使った場合だけであり、この例の場合、右側はthis_model を使わずに Model名を書いているので、OKだということでしょう。




ctr_dist( this_model.Man_A.kine.global.posz, Model.Man_B )

これも大丈夫でした。 Model名を使っている右側のみオブジェクト名で終わらせています。やはり、this_model さえ入ってこなければどの書き方でもかまわないように見えますね。 this_model が登場したときのみ、posx などと最後まで書かなければいけないということのように見えますね。



ただしですね、

ctr_dist( this_model.Man_A.kine.global.posz, Model.Man_B. )

このようにピリオドを入力すると、別にエラーは出ないものの、ピリオドが勝手に消えて Model.Man_B で終わる記述が保存されます。 これも納得行かない挙動ですが、まあ、ピリオド無しでちゃんと動くのだからまあいいとしましょう。






しかし一方で、


ctr_dist( this_model.Man_A.kine.global.posz, this_model.Man_B )
ctr_dist( this_model.Man_A.kine.global.posz, this_model.Man_B. )
ctr_dist( this_model.Man_A.kine.global.posz, this_model.Man_B.kine.global.pos )




これはダメでした。 左側だけ this_model + posx まで書くやり方にして、右側は this_model を使っているにも関わらず posx まで書いていないという状態です。 これだと、エクスプレッションはセットされませんでした。

つまり、たとえ片側(この場合左側)で posz まで書き切っていても、その側だけが成立しているに過ぎず、もう片側も成立しなければ全体としてエクスプレッションが完成しないのでセットされない、と解釈できると思います。









以上。

一応、ここまで分かってれば、仕事で困ることはなさそうな気がしますね。

それにしても、謎の挙動ですね。

普段は 「マニュアルの記述に反しているとまでは言えないので不具合ではない」 などとお役所のような言い方をし出すこともある俺が尊敬する某空調屋さんに勤務の某Sさんでも、きっとこれは不具合だと言うと思います。 でも不具合かどうか、正式にバグとして登録されるのかどうかは、もちろん将来的には重要ですが、今この瞬間はどうでもいいです。 Let's get this fuckin' job done. 現場はこれだけを考えていますからね。






ということで、剛田さん、193さん、りゅーぞーさん、m4g さん、ええと他にもいたっけ、実験に協力してくれた皆様、本当に有難う御座いました。俺一人ではここまでたどり着けませんでした。 是非また次もお願いします。 みんなでこの馬鹿野郎ソフトウェアをねじ伏せましょう。


そして、実験していないお方も、是非一度、上の実験を真似てやってみることをオススメします。 というのは、俺もいつもそうなんですけど、「ふーん、これは役立つ情報だな。ブックマークしておこう」 などと言ってブックマークしても、自分で実験していないと、あっという間に忘れちゃうんですよね。 ブックマークしたという事実も忘れるし、それどころか、そういう問題があると誰かが言っていたという事実までも忘れてしまいます。 でも一度実験すると、体が覚えます。





まだこういう実験ネタ、なんぼでもあります。

ブログに書くのもそれなりに大変なので、しばらく寝かせているうちに腐ってしまうネタの方が多いですね。 ほんとは鮮度が重要なんだよなあ。


15分くらいでブログ記事1本書ける能力が欲しい。
昔と比べてだいぶ速くなったけど、それでもやっぱり30分や1時間はかかることの方が多いです。
少しくらいはまとまりを持たせようなどと色気を出さずにただ書きなぐればいいのかな・・・



ともかく鮮度を失わないうちに書く。
そのためには、速く書ける能力が必要。
速く書ける能力=問題の本質部分だけを一瞬で抽出して無駄のない簡潔な文章と構成が自動的にスラスラ出てくる能力。


そんな能力磨いているヒマは俺にはねえよヴォケ
このクソったれソフトウェアと戦うだけで精一杯なんだよハゲ







.

| | コメント (0) | トラックバック (0)

2012年1月13日 (金)

乳トラルポーズでポン。

深夜に唐突に降って来るスクリプト。






//    乳トラルポーズでポン。
var oNPCol = XSIFactory.CreateObject( 'XSI.Collection' );

var oCol = XSIFactory.CreateObject( 'XSI.Collection' );
oCol.items = Selection;
var oExpanded = oCol.expand( );

for (var i=0; i < oExpanded.count; i++ )
{
    NPFlag = false;
    var oObj = oExpanded(i);
    if ( oObj.IsClassOf( siX3DObjectID ) )
    {
        var oLK = oObj.Kinematics.Local;
        if ( oLK.nsclx.value != 1 || oLK.nscly.value != 1 || oLK.nsclz.value != 1 )
        {
            NPFlag = true;
        }
        else if ( oLK.nrotx.value != 0 || oLK.nroty.value != 0 || oLK.nrotz.value != 0 )
        {
            NPFlag = true;
        }
        else if ( oLK.nposx.value != 0 || oLK.nposy.value != 0 || oLK.nposz.value != 0 )
        {
            NPFlag = true;
        }
        else if ( oLK.nsclorix.value != 0 || oLK.nscloriy.value != 0 || oLK.nscloriz.value != 0 )
        {
            NPFlag = true;
        }
    }
    if ( NPFlag == true )
    {
        oNPCol.Add( oObj );
    }
}
SelectObj( oNPCol );











ニュートラルポーズが入ったオブジェクトを見つけます。 たぶん。


シーン内のオブジェクトを、ブランチとかノードとかグループとかで選択した状態で実行します。 すると、なんらかのニュートラルポーズが入っているオブジェクトだけが選択された状態になります。 選んでいたもののうちから検索してます。シーン全体でやりたければ、シーンルートをブランチ選択すれば良いでしょう。たぶん。






ニュートラルポーズがある状態とは、Local KinematicsNeutral Pose タブで、SRT が 1,1,1  0,0,0  0,0,0 になってない状態ということで合ってますよね?  あと、それに加えてシアー(Scaling Orientation)か。


一応、それを前提に検出しています。
間違ってたら教えてください。











同じようなスクリプト、以前に書いたことあるはずだよなあ・・・・。


リギング用小スクリプトがわんさかあるんですが、っていうか毎日少しずつ書き足しながらやってる感じですが、だんだんカオスっぷりがひどいことになってきて、自分で書いたスクリプトをHDDから探したり改良したりするのがめんどくさくて、一度書いたことがあるものでも難しくなければもうゼロから書き直してしまったり。  ちゃんと整理しておけばすぐ使えるのにねえ。 


こうして無駄な繰り返しを繰り返し繰り返し繰り返すのです。スクリプト書いて省力化しているつもりで、実はたくさん無駄なことをやっているという、微妙な感じです。



.

| | コメント (0) | トラックバック (0)

2012年1月11日 (水)

ctr_dist と this_model の軋轢。

なぜか、ctr_dist の中で this_model が使えないんですよ。
誰か実験してくれませんんか。



エクスプレッションの ctr_dist です。2つのオブジェクトの間の距離を返してくれるという、すごく便利なエクスプレッションです。 今回はこんなエクスプレッションを書きました。

Thismodel1

ctr_dist( Model.Man_A, Model.Man_B )

Mac_C のスケールXにエクスプレッションを仕込みました。  Man_A と Man_B の距離をスケールXにブチ込むので、A や B を適当に横に動かして両者の距離を広げたり縮めたりすると、それに合わせて Man_C のスケールXが変化するというものです。 なんてことありません。


Man_A, Man_B, Man_C は、いずれも Model という名前の Model に所属しています。なのでオブジェクト名のところは、"Model.Man_A" のように、Model名を含めたフルネームになってます。 これで何も問題ないです。 ちゃんと動いています。 




でも今回は、例えば Action 化して汎用的に使おうなどという場合のことも考えて、this_model を使いたいんです。"Model" などという、汎用性の無い特定の名前を記述してしまうのではなく、「名前はどうでもいいから、そのオブジェクトが所属している Model 」 という記述にしたいのです。 だからこそ、this_model です。 そのためにある this_model です。まあ、そのためだけではなく、例えば Model名を覚えていなくても this_model で通っちゃえばラクだから、という意義もあると思いますが、いずれにせよ this_model は便利で有用なものです。




ってことで

ctr_dist( this_model.Man_A, this_model.Man_B )

と書いたのですが、

Thismodel2

効かないんですよ。


「効かない」
 というのは、エクスプレッションがセットされないという意味です。 Apply 押してもガン無視されます。 まだエクスプレッションが何もない状態でこのように書くと、ガン無視され、既に何かエクスプレッションがセットされていた場合は、元のエクスプレッションのまま更新されていません。つまり、やはりガン無視されているということです。


いろいろいじってみて、今のところ、

 ctr_dist のカッコの中では this_model は効かない
 それ以外では、ちゃんと this_model が効く

という風に、俺には見えるんです。



誰か実験してくれませんか。

とりあえず、this_model を使わない状態でちゃんとエクスプレッションが効いている状態のシーンをアップロードしておきました。 XSI のバージョンは 2012 です。

ダウンロード CtrDistAndThisModel.zip (114.9K)

これを this_model に書き換えられるかどうか、試してもらえませんかね?






それと、このシーンで試しただけだと、俺がこのシーンを作る時に何か変なことをやらかしている可能性を否定できなくなるので、できれば、まっさらな新規シーンの状態から、ご自分でこの問題を再現させられるか、試してもらえませんか? なにとぞご協力を。


まあ、this_model 使わなくてもなんとかなりますけどね。 でも Action 化して他の Model で Apply した時とかに困るんだよな。 リギングしてると、後半になって全体の整理のために新しい Model に移したりとか、よくやるから。 その時エクスプレッションが、古い Model の方を参照しようとしていたら困るから、確実に新Model 以下のオブジェクトを参照している状態にしたい。新Model に親子をつなぎ直すだけなら、エクスプレッション内の参照先の記述は自動でアップデートされるから問題ないと思うんだけど、Action 経由にすると、当然 Action 内は自動でアップデートされないから、困るんですよねえ。





ともかくだ、ctr_dist は、本当に this_model と相容れないのかどうか。

これがバグだろうが仕様だろうが今はどうでも良くて、どんな環境下でも現象として常に再現するのかどうかを、まずは調べたいのです。





俺ひとりでは、なかなか結論を出せないというか。 どうしてもワークフローの手グセもあるし、もろもろの初期設定など条件が固定されたもとでは、客観的な結論を出しにくいです。 なので、色んな人が、それぞれの環境下で実験して、それでも再現するということが確認できたら、


  嘔吐デスクどうしかしろやゴルァとか、

  今はこういうもんだといったん受け入れて、代替ワークフロー考えようぜオルァとか、

  次にこれに出くわしても焦らないぜもう分かってるぜさっさと this_model あきらめて実Model名使うことにするから無駄な実験の時間はかかんないぜノルァとか、


こんな感じで建設的に次のアクションに進めます。 まずは現象として再現するかどうかの確認が、第一歩です。

どうかご協力をお願いします。










こんな感じで、俺の代わりに誰かに実験をやらせる今さら聞けない素朴な疑問を誰かに質問したり、お互いに協力して実験したりする空気ができるといいなあ、などと妄想しております。 




俺の場合、SI|3D が XSI になった頃から、ひとりで実験したり開発したり、必死で英語の情報を漁ったりしながら、誰にも聞けず悶々としながら何年も過ごした時期がありました。 ひとりで調べて、ひとりで実験して、ひとりでワークフローを決めて指示を出したり。 まわりにダメ出しをしてくれる人はいなく、俺様流で文句も言われないのである意味ではラクではありましたが、すごく孤独感があり、つらい感じがしてました。  「これ、オレ流過ぎるよな?」 「普通どうやるんだろう?」 「俺、自己流を通してばかりで、時代に置いていかれてる気がする」 「誰か俺に教えてくれよ。毎日調べ物と実験だけじゃ、仕事進まねえよ」 などとストレスを溜めていました。  

インターネットでの交流がさかんになってきたので、それを利用して、グダグダにならずにCGの情報共有ができる場所を作ろうなどと妄想して実験したこともありました。



しかしここ1~2年で大きく状況が変わった感じがしています。ツイッタとかで質問もできるようになったし、個人的にだいぶ知り合いも増えたおかげで直接質問したり意見を聞いたりすることもできるようになりました。 一時期に比べれば夢のようです。 XSI Base のスレッドに深く潜ってってどうにかするのが唯一の方法だった 2003年とか 2004年頃を懐かしく思い出します。


ただし、ツイッタは気軽でいいのですが、常に見ていないとすぐに情報が流れ去ってしまう感じがしていて、CGの話の情報交換場所としては俺はまだ上手く活用できていません。欲しい情報だけフィルタする方法がわからんと言うか。  ツイッタの利用目的も皆バラバラなので、そこが良い所ではあるのですが、欲しい情報は埋もれてしまう傾向にありますよね。原発やセシウムや東電のことばかり喋りたい人もいるし、格言や名言みたいなのが好きな人もいるし、食い物の話とかも出るし、性器の名称を連呼するような馬鹿野郎もいるし、すげえカオスです。 このカオスの中で自分の目的に合致するよう情報をフィルタするのは至難の技です。

また、あまりにも気軽過ぎるゆえに、何か言うにしてもついついテキトーなことを言ってしまいそうになりますよね。 誰かに質問された時などに、自分で改めて実験もしてないのに、テキトーな記憶を頼りに 「こうじゃなかったっけ」 みたいなことをついつい言ってしまいます俺。 すいませんすいません。 でも、その気軽さゆえに情報を引き出すのが簡単なのがツイッターのいいところなので、気軽で無責任な発言だったとしても否定はしないしむしろ歓迎したい気分です。 でもそれだけではいけない。こうしてブログなどにベイクしておくことも重要だと思っております。

気軽で活発なやりとりから関心や興味が芽生え、複数の人がもう一段階踏み込んで実験し、結果を教え合い、最後にブログにベイクというのが理想ですかね。 いや、そんな上手くいくわけないな・・・。  ともかくですね、ひとりでやっていてもダメなんです。そんなんじゃあの暗黒時代に逆戻りです。 皆さんとやりたいんです俺は。







あれ? なんか話が硬くなってるよ。
この記事書き始めた時点ではそんなつもりは全くなかったはずなのに。
ctr_dist ゴルァしか考えてなかったのに。

おかしいな。


まあいいや。

早く俺の代わりに実験して下さい。







.

| | コメント (2) | トラックバック (0)

2012年1月10日 (火)

ブールのトグル。

どうでもいいネタいきますよ。


しかも、プログラマやスクリプト書きの人たちには笑われるような話ですがね。
お前そんなことも知らなかったのか、と。


まあいいんです。 今年でみんな死ぬんだから。
もうとことん恥を晒してもいいと、決めたんです。






いや、単に、スクリプトでブーリアン値のトグルをしたい時に、こんなに簡単に書けたのか、というだけ話です。 しかも JScript 限定の話ですね。

ちょっとスクリプトの調べものをしていて、SDKガイドのサンプルスクリプトでそういう書き方になっていて、ハッと気付きました。


hoehoge.value = !hogehoge.value;




こんだけです。
すいません。




なるほど、オンならオフに、オフならオンにしたいわけだから、元の値を ! でひっくり返せばいいというわけですね。 ! って 「反対の」 とか 「非」 の意味ですよね。なるほど、今までも条件分岐その他でさんざん使ってきたからその意味は知っていたけど、こういう風に値をセットする時にも使えるという発想が、なかったです orz

俺、1行スクリプトとかではない、ある程度まとまったスクリプトを書き始めたのが 2005年だったと思うんですよね。 ってことは7年前ですか。 そんだけ経っているのに、こういう書き方ができるなんて、今の今まで知りませんでしたよ。



恥を晒しますけどね、今までは、


if ( hogehoge.value == true )
{
    hogehoge.value = false;
}
else
{
    hogehoge.value = true;
}





こんな風に書いてました orz
阿呆か俺は orz
1行で済むじゃねえかよ orz
知らなかったよ orz



知らなかったというより、こういうのって、センスですよね。 センスとかスジのある人って、1か2を体験するだけで10できるようになっちゃうわけですね。想像力の豊かさ、頭の回路のつながり易さ、ですかね。  俺なんかは、10のことは、1から10まで全部実体験として遭遇しないと身に付かないんですね。 ええ、そんな野郎ですよ。 どうせ皆死ぬけど。






PPG の Logic の中でも、似たような無駄をいっぱいやってましたねえ。
この書き方があればずいぶんスッキリします。



例えばこういうやつね。


var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "はげ";
oP.AddParameter2( "bHage", siBool, true );
oP.AddParameter2( "sHage", siString, "はげはげ~ん" );

var oL = oP.PPGLayout;
oL.AddItem( "bHage", "はげちょびん。" );
oL.AddItem( "sHage", "このハゲ野郎" );

oL.Language = "JScript";
oL.Logic = OnInit.toString() + bHage_OnChanged.toString();

function OnInit()
{
    bHage_OnChanged();
}
function bHage_OnChanged()
{
    PPG.sHage.ReadOnly = !PPG.sHage.ReadOnly;
}

InspectObj( oP );


Hagehage

ブーリアンスイッチをオンオフすると、テキストフィールドのリードオンリー属性、つまり書き込み可・不可がトグルします。 今まで色んなスクリプトでさんざん書いてきた処理ですが、そのたびに、 オブジェクトモデルで書くとブーリアンのトグルが麺独裁よなあ、コマンドなら ToggleValue 使えてラクだけど、でもコマンド嫌だなあオブジェクトモデルだけで書きたいなあ。  などと悶々としていました。 阿呆か俺は orz  オブジェクトモデルでも1行で書けるではないか。

↑あー ちなみにリードオンリー属性のトグルは ToggleValue コマンドでは出来ません念のため。ここはどのみちオブジェクトモデルでの書き方が必要になると思います。 ToggleValueコマンドで書き換えることができるのは、ブーリアン形パラメータのであり、リードオンリーかどうかなどのパラメータの属性をいじるコマンドは無いと思います。 上の書き方だと、コマンドでもリードオンリー属性がいじれるけどコマンドが嫌だからオブジェクトモデルにしてました、という風にも読めるので、そういう意味ではないですよという補足でした。



こんなのもアリか。

Selection(0).Properties( "Visibility" ).viewvis.value = !Selection(0).Properties( "Visibility" ).viewvis.value;

選択しているブツの View Visibility をトグルしています。 ToggleVisibility コマンドのオブジェクトモデル版みたいな?





まあ、ともかくですね、今年はみんな死ぬということが言いたかったのです。
そうなんです。






ところで、 Python や VBScript では、同じように簡単にブール値をひっくり返す書き方って、できますかね?  俺、いちおう母国語としている JScript ですらこんなですから、他の言語は完全にわかりません。




.

| | コメント (5) | トラックバック (0)

2012年1月 9日 (月)

ロードでポン。

いや、某所で某さんが叫んでいたので、簡単にできないかな~と。


完全に無断で、全文ママ引用しますが、


エーリック!!
頼むからサンプルシーンはリピートオフっといてくれ!!
何度気付かずにシミュまわしたことか!



だそうです。



em シリーズのエリックさんのことだと思われます。

emFluid や emVolume などで、クソ重いシミュレーションのサンプルシーンを添付する時は、再生リピートオンの状態でセーブしてくれるな、クソ重いんだから再生止められなくなるだろゴルァ  ということだと思います。 でも独逸語で言わないと通じないかもしれません。 だんけしぇーん。 通じたかな。






ならば、シーンをロードし終わった瞬間に再生リピートを有無を言わさずオフってしまえばいいのではないか、と思ったんですよね。 


そういうことをするためにある機能が、カスタムイベントだと思います。


 新規シーンを作ったら、自動でこれしなさい
 レンダ終わったらこれしなさい
 インポート始めるときは先にこれをしなさい
 シーンのロードが終わったらこれしなさい


みたいな、何かが始まるとき・終わるときに XSI 様にやって頂く行動を、スクリプトで書いておくというものです。 始まる・終わるに限らず、トリガになるイベントはいっぱい用意されてますね。 選択しているブツが変わったら、とかいうイベントもありますね。 



俺の場合、大昔に撲滅アンビエンスというプラグインでカスタムイベントを使ったことがありました。その他も数回使ったことがあったけど、何年ぶりかで使い方忘れてました。なので調べてみたら、簡単にできました。



ロードでポン。

ダウンロード LoadDePon.js (0.8K)


どんなシーンでも、ロードが終わった瞬間に、スクリプトで書いたことが実行されます。 今回の場合は、サンプルとして「再生リピートをオフにする」というスクリプトを埋め込んでいますが、そこを書き換えて使えばいいと思います。





短いのでソース丸ごと載せます。


----------------------------------------------------------------

function XSILoadPlugin( in_reg )
{
    in_reg.Author = "junki";
    in_reg.Name = "LoadDePon Plug-in";
    in_reg.Major = 1;
    in_reg.Minor = 0;

    in_reg.RegisterEvent("LoadDePon_EndSceneOpen",siOnEndSceneOpen);
    //RegistrationInsertionPoint - do not remove this line

    return true;
}

function XSIUnloadPlugin( in_reg )
{
    var strPluginName;
    strPluginName = in_reg.Name;
    Application.LogMessage(strPluginName + " has been unloaded.",siVerbose);
    return true;
}


// Callback for the EndSceneOpen event.
function LoadDePon_EndSceneOpen_OnEvent( in_ctxt )
{
    Application.LogMessage("LoadDePon_EndSceneOpen_OnEvent called",siVerbose);

    Application.LogMessage("FileName: " + in_ctxt.GetAttribute("FileName"),siVerbose);

    Hage();
   
    return true;
}

function Hage()
{
   
 //    ここを書き換えるんだよハゲ
   
SetValue("PlayControl.Loop", false, null);
}


----------------------------------------------------------------

SDK ウィザードってやつで作りました。チョー簡単でした。

セルフインストール形式のプラグインです。 なのでスクリプトエディタで走らせるようなものであはありません。 Plugins フォルダにファイルをブチ込んで、XSI を再起動するか Plugin マネージャから Reload して下さい。

プラグインフォルダってのは例えばこういう場所です。
C:\Users\<あんたのユーザ名>\Autodesk\Softimage_2012_SP1\Application\Plugins





で、緑の字の部分 = Hage ファンクションの中の、赤字の部分が実際の処理なので、ここを自分の好きなコードに書き換えればいいだけです。 何行書いてもいいし、外部ファイルを呼び出してもいいし。

今回の場合は、再生リピートをオフにしたいという例なので、上のような書き方になってます。 実際に XSI のインターフェース上で再生リピートボタンを何度か押してみて、スクリプトエディタにログされた結果をコピペしただけです。 

これで、うっかり者のエリックくんが再生リピートオンのままセーブしてしまったシーンでも、このカスタムイベントが効いている限り、ロードが終わった時点で必ず再生リピートはオフになります。 独逸語を勉強せずに済みました。 デコのスペキュラにも磨きがかかります。



ちなみにこのカスタムイベントを無効化したい場合は、上記のファイルを Plugins フォルダから削除してもいいし、プラグインマネージャから一時的にミュートしてもいいです。

Mute





さて、現実逃避はここらでやめて、仕事に戻りますかね・・・・・。
ああ、仕事と関係のないスクリプト書きって、なんて楽しいんでしょう。

.

| | コメント (0) | トラックバック (0)

2012年1月 8日 (日)

スケの色。

リギング作業していましてね。





そしたら、スケマティックビューが、こんな色になっちまったんです。

Schecolor1

何も選択してない状態ですよ。
本来は、ライトはオレンジ、ヌルは緑、とかじゃないですか。
まるでブランチ選択した時のような色になってしまっています。



たまにこの現象、起きていたような気がしないでもない。

これじゃ作業になりません。

XSI を再起動しても変わらず。

マシンを再起動しても変わらず。



困り果てました。


え? 困らない?
スケマティック使わないから、困らないって?


暴言いきますよ。


スケマティックを使わない XSI 使いは、うんこたれです。

だって、コンストレインとかエクスプレッションの関係とか、どうやって見るんですか。
エクスプローラだけで、シーンの中の何がどうなってアレで、って全部把握できるんですか。
へえ、そりゃすごい。すごいうんこたれですね。

最近の若僧はスケマティックを使わないからダメなんだよ。
基本がなっちょらんよ。
エフェクトがどうしたとか、リミテッドアニメがどうとか、
ヘアがとかクロスがとかボリュームシェーダがとか言う前に、
まずはスケマティックを使えってんだ。
全てはそこからだ。
日本スケマティック協会発足。

暴言終了。






ともかく、リグ作業やっててこんな色のスケマティックじゃ話になりません。オブジェクトにどんどんワイヤカラーを付けていきますしね。 ワイヤカラーでブツの種類など整理しますからね。 



とか言ってたら。

あ。

Schecolor2

Use Diffuse Colors になっていただけでした。
Use Wire Colors にしたら、元に戻りました。



いや、そんだけですよ。
ええ、自分用メモですメモ。 よくあるメモってやつ。


2012年最初の記事がこれだから、もう、ダメダメですね。 このブログも俺も、終わってますよ。 今年はジタバタせず、皆様と一緒にちゃんと滅亡しますね。




曲行きましょう。



まだまだテイラーたんブームは続きますよ。

いやあ、良い。 どの曲も良いなあ。。。。
ほんと、どの曲も良い。 イマイチの曲なんて、無い気がする。




皆様。
明けましてお目出度う御座いました。

| | コメント (0) | トラックバック (0)

« 2011年12月 | トップページ | 2012年2月 »