« 2012年5月 | トップページ | 2012年7月 »

2012年6月

2012年6月29日 (金)

ICE で複数メッシュのマッチング。

久しぶりに XSI Base をのぞいてみたらなにやら危険サイト指定を受けていて焦ったが、どうやらすでに対処は済んでいるように見えました。 ほんとに大丈夫なのかな。 いったいどうしたのだろう。 どっかの阿呆がスパム的な何かを仕込んだのかな。




そんなことはどうでもよくて、ええと、頂点数も頂点の順番も全く同じ2つのメッシュで、片方のオブジェクトの頂点をもう片方に完全にマッチさせる(同一座標にする)には、とかなんとかの話が出ていたので、仕事したくない気分なので練習というか研究というかサボりというか確認のために、真似してやってみました。 このスレッド。

エリックさんは Switch Context を使えという。
やってみたらできた。
うん、簡単だ。

もし Switch Context を使わなければどうなるか、
をやってみたら、こうなった。
というメモですこの記事は。

Array_vs_switchcontext

クリックすると別ウインドウにでかい画像が出ます。


男が2人います。
男1がご主人様です。
男2は奴隷です。
奴隷の頂点は、ご主人様の頂点にぴったり一致して着いて行かねばなりません。

ということにします。



よって、従わせたいのは奴隷である男2の方なので、男2に ICETree を作成します。 その ICETree の中で、


 Get 男1のPointPosition → SwitchContext → 男2に SetPointPosition


とやればできました。 画像の の流れですね。 男1の頂点をぐにぐにいじると、男2の頂点も同期して動きます。 完全に一致して動いているように見えます。 なるほど、これ便利かも。


考えてみれば、ICE を使わずにこれをやろうとすると、Clone ってことになりますかね? でも Clone は頂点を数式的に指定してオフセットさせるとか簡単にできるわけじゃないし、ICE でやるメリットは大きそうな気がしますね。






ここでちょっと話が逸れますが、ツリー中の、この記事の本題とは関係ない部分について先に説明を済ませてしまうと、


前にもやった例の 「オブジェクト自身のSRT にも追従する」 をやりたいので、前半で Matrix と掛け算しています。 これで、頂点でなくオブジェクトを SRT した場合でも完全に追従します。たぶん。

それと、途中 Add を挟んで結果の頂点位置をちょいわきにオフセットさせてます。 これは単に、頂点位置が一致しているとビューポート上で二人の男がぴったり重なって見づらいからというだけです(まさにその頂点位置の一致こそがこのツリーの目的なんですがね)

それと、Aの流れとBの流れを確認するためにいちいちつなぎ直しが発生するのは面倒なので、if ノードで分岐させてます。 true にしている時はこっちの流れのツリーから来たデータがセットされ、false のときはこっち、という、確認を楽にするためのアレです。






本題に戻りますが、SwitchContext というのは魔法のようなノードで、その名の通り Context 違いでつながらないポート同士を無理にでもつなごうとするノード、という認識で合ってますかね? なんとなくそんな風に思っているんですが・・・・・ツッコミお待ちしております。ツッコミ歓迎です。



今回の場合、男1の頂点情報を男2の ICETree に持ち込みたいわけです。 最終的にセットされるのは男2の頂点情報ですから、その情報は男2が所有するものでなくてはなりません。 しかしこの例だと参照する元のデータは男1のものです。 男1のデータはそのままでは男2に入ってくれません。 なのでここになんらかのコンバートが必要になります。 そこで SwitchContext の登場。 こいつが男1の情報を男2のものとして変換してくれるという。  ええと、こんな解釈で合ってますかね? 間違ってたらどうかツッコミお願いします。




ってことで、上手く行っているように見えます。簡単だし、ノードが少なくて良い。




でもまあ、不思議な魔法でつなげているようで気持ち悪いと言えなくもない。 なので違う方法でできねえかなーってちょっといじってみたらできちゃったような気がしたのが、上の画像の の流れです。 男1の頂点位置をいじると男2の頂点も追従します。Bの SwitchContext でやった場合と全く同一の結果を得られます。たぶん。


このAの方法では、上流から流れてきた男1の情報を、Build Array from Set でアレイにブチ込んでいます。このアレイは男2の ICETree 上で構築しているものですから、これは最初から男2が所有する情報ということになります(中身は男1から来ているけれども)。よって下流のノード(男2の情報を要求しているノード)につなぐことができるようになります。 この辺の説明アヤシイかな。。。。


で、そのアレイの中の何番目のものをセットするか、の部分で Get Data を使って男2自身のVertex Index を参照しています。 頂点の順番情報ですね。  「アレイの中の最初の情報は自分自身の最初の頂点にセットするぜ。 アレイの中の10番目の情報は自分自身の10番目の頂点にセットするぜ」 ということですね。 つまり、男1も2も同じ頂点数、同じ頂点番号を持つということが絶対条件になっている ICETree ですね。





ってな解釈で合ってますかね?
分かってないのを見透かされそうで、毎回ビクビクもんですよ。 
ツッコミお願いしますね。



ま、多少解説が怪しくても、結果的にやりたいことが出来ていれば、こうしてメモしておけば後でカンニングできます。

でも、なんにせよ解説を正しく出来ないってことは理解してないってことだ、逆に言えば人に正しく解説できるってことはちゃんと分かってるってことなんだ、という思いがいつもあるので、美しい解説とか厳密に正しい解説を追及すること自体は良いことのはずだ、などと戯言をタレているヒマがあったらこのリテイク修正早くやれよブログ書いてる場合かよ仕事サボんなよ俺。



とかなんとか。





.

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

2012年6月27日 (水)

乳Project。 不要フォルダ無しで新規Project作成なプラグイン。

XSI のプロジェクトって、いっぱいサブフォルダありますよね。
使ったこと無いフォルダも多い。 Fcurves とか使ったことねえなあ。



なので、新規Project を作る時に要るフォルダを任意に選べるといいなと思っていたんですよね。



というプラグインです。



乳Project
http://homepage3.nifty.com/jjj/XSIFiles/Plugin/JJJ_XSI_Plugins.html

Nyuproject_ppg


要るフォルダのみチェックをオンにしてボタンを押すと、チェックしてなかったフォルダは存在しない XSI Project が出来上がります。





そう言えば、現在の XSI と昔の XSI で、新規 Project を作った時の挙動がちょっと違う気がするんですが、どうですか。  昔は、新規 Project を作成した瞬間に、作成したばかりの Project がアクティブな Project になり、かつ、その時開いていたシーンは名称未設定になっていたと思うんですよ。 なので、そのまま上書きセーブしようとしても上書きにはならず、保存のダイアログが出ます。 そして新規 Project の Scenes フォルダがデフォルトの場所になっていたと思うんです。

この挙動の方が好きだった、というか慣れてしまっていたので、一応それに近いと思われる状態を再現できる機能も入れておきました。



Scenes フォルダと system フォルダはオフにできません。 必須ですたぶん。 Scenes はシーンファイルを保存しない XSI Project を作ろうというなら無くでも大丈夫かもしれませんが、そんな Project はあるんだろうか。 

あと、シーンのバージョン履歴を残す場合、Backup フォルダは無くても勝手に作られちゃいますね、



とかなんとか。






.

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

2012年6月15日 (金)

貴方といっしょにいさせて( *゚д゚)♂ 。 プラグイン化。

去年書いたツールなんですがね。

あなたと一緒にいさせて( *゚д゚)♂ 。






その後、あまりにもよく使うので、若干整理してプラグイン化しました。

http://homepage3.nifty.com/jjj/XSIFiles/Plugin/JJJ_XSI_Plugins.html

Anatatoisshoniisasete_instruction

もし使ってみようというお方がいらっしゃるならば、詳しい説明は、この記事の冒頭に貼った昔の記事へのリンクか、プラグインのページの説明を読むと分かると思うので、そちらを読んでみて下さい。 

簡単に言えば、「このオブジェクト(複数可)を、 すでにシーンに存在するあのオブジェクトと同じ Layer に入れたい, 参加している Group も同じにしたい, Partition も全 Pass で同じにしたい」 っていう時に全部イッキにやってくれるという、そういうツールです。 っていうか難しく言いようもないですねw  これが全てです。




仕事のスケジュールの都合上、モデルが決まらないうちにアニメーションを始めたり、後で差し替えが発生することを前提にコンポまで組んでしまうことも多いじゃないですか。

あるいはスケジュールの都合じゃなかったとしても、ショット作りの初期において、あまり後のことを考えずに、その場その場の思いつきや都合で Group にどんどん入れてったり、Pass 分けまでしちゃったり、そういう風に進めたいことも多いじゃないですか。



こんな風に、最初から必要な要素が全て綺麗にそろっていてそのデータのまま最後までショットが完成することは少なく、最初は未整理あるいは未決定のまま始めてしまい、整理や意思決定をしながら作り進めるため、どうしても後から差し替えや追加が必要になります。 その作業を楽にしようというのが、このツールです。


俺は劇的に楽になりました。当たり前です。俺様が楽をできるように書いたのですから。





あとは、レイヤ、グループ、パーティション、親子関係、この4つ以外で、差し替えや追加のオブジェクトに対して再現したい属性って、何がありますかね? 思いついたら、というか作業していて欲しくなったら、また追加しようと思っています。





.

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

2012年6月14日 (木)

Seed を変えると発射。

花火の記事にコメントを書いてくれたミツモトさんが教えてくれた、スヴァらしい tips をメモしておきます。


その花火の記事の中で俺が、


  「エミッションのモードが Total Number of Particles の時、複数回発射させることはできるのでしょか?」


と疑問を投げかけています。 ミツモトさんがコメント欄でそれを教えてくれたのです。


Total Number of Particles モードで発射すると、通常は最初の1回しか発射できないじゃないですか。 任意のタイミングで何回も発射できれば便利なのになーって思ってたんですよ。

いい方法を知らなかったのでこれまでは仕方なく、、Number of Particles Per Second モードを使い、あるタイミングでイッキに Rate を上げてすぐ次のフレームで下げる、とかやってました。  このモードだと 「1秒間に発生する数」 ですが、例えば 24fps で作業していた場合に1フレだけ発生させたら24分の1の量になるわけではないので、ズバリそのフレームのタイミングで何個発生するかは、運任せでした(たぶん)。 


ですが、ミツモトさんは、Total Number of Particles モードにおいて、Seed にキーを打って数値を変化させると、そのキーのタイミングでまた発射させることができるとおっしゃるのです。


  なんですと (゚∀゚)



さっそくやってみたら、そのまんまできました。

Emitparticlesonseedchanged




シーンファイル(ZIP)のダウンロード(XSI 2012)





複雑でも何でもないので、あんまり解説するほどでもないですけどね。
Seed にキーさえ打てば、何度でも発射できました。


Aで、モードをそれにしています。

Bにキーを打ちます。

何もしないと、シーンの最初のフレームが1発目の発射になってしまいますが、Cをオフにしておいて、任意のフレームでオンにするキーを打ってあげれば、そこが1発目の発射になります。 画像の下の方、FカーブのCがこのキーです。

Fカーブのキーフレーム間の補完モードは、基本は Stepped で良いでしょう。値がいくつなのかは全く重要ではありません。 値が変わりさえすれば、発射されます。

FカーブのEの部分、ここは値が変わってないキーを打った部分です。値が変わってないため、発射されませんでした。 つまり、キーがあることが重要なのではなくて、Seed の値が変化することが重要だということになります。

Dの部分は補完を Linear にしました。つまり、次のキーフレームまで毎フレ値が変わります。 この部分では、毎フレ発射され続けました。




  いやあ、便利じゃないですかこれ ヽ(゚∀゚)ノ



これだけで、どれだけエミッションタイミングの自由度が広がるか。大違いですよ奥さん。

まだ仕事でバリバリ使ってないので、どこかに落とし穴があるかも知れませんが、それにしてもお手軽にエミッションタイミングをいじれるのは大きいです。


そのフレーム上における発生数(Rate)は、Rate というパラメータでズバリ指定しているわけだから、数に関しては最初から正確にコントロールできるモードですよね。 そして今回の方法で、タイミングも正確にコントロールできるわけです。 値を変え続けることによる毎フレ連続発射も可能ですね。  あとは、発生場所さえ自由にコントロールできれば、もう、かなり神に近づいたと言えるのではないでしょうか。そんなことないですか。そうですか。


Add Point やらなんやらで全く独自のエミッションをゼロから構築できる人はそうすればいいと思うんですが、俺などはもちろんそんなことできず、いつも Emit from XXX コンパウンドを使います。 この仕組みがわかっておられるお方、その仕組みを元にもっと良い方法を知っておられるお方は、是非教えて下さいませんか。



いやあ、早くこの手法を仕事で試してみたいですね。 仕事で使わないと検証にならないですからね。 楽しみですね。

ミツモトさん、ありがとうございました (゚∀゚)












それはそうとミツモトさんのブログを見てみると・・・・・



あーーー! ぱひゅーむのやつじゃないですかこれ。
すげえ、良く出来てますね!
XSI ですかこれは?
俺もやってみたかったんですよこのパヒューム踊り。
モーキャプデータと音はダウンロードしてあるんだけど、
俺、モーキャプデータを XSI でどう扱うかとか恥ずかしながら知らなくて、
そのうち調べながらやってみようかな、くらいで放置していたんです。

ううん、これ見て、やりたくなっちゃったなあ。
凝ったもの作る気はないけど、やはり、男を踊らせてみたいじゃないですか。

コンポでもけっこう手を入れてそう。
残像感のあるブラーかエコーみたいなやつは、後処理かなあ?
後半のスタジオっぽくなってるところの発光系も後処理?
天井ライトのレンズフレアは3Dからトラッキング情報とか出して2Dで?
それともICE制御だったりして?




それこそメイキング記事書いて下さいよミツモトさん



.

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

2012年6月13日 (水)

友愛 その11。 プロパティを View で表示させる。

PPG をいかに小さくするかに命をかけることもあります。


ウインドウ開きっぱなしで作業するタイプのものは、大きいと邪魔ですからね。 XSI の UI が、もっとドック的に埋め込みカスタマイズの自由度が高ければいいんですけどねえ。  AfterEffects のあんな感じみたいに自由にならないかな。フローティングではなくどこかに埋め込みたいんだよな。 あるいは大きなフローティングウインドウの一部に、その時々によって任意に埋め込んだり切り離したりしたいんだよな。



まあ、できないことを嘆いても仕方ないので、フローティングウインドウを我慢して使うのですが、小さいほど嬉しい場合が多いです。 なので過去の友愛シリーズで色々書いたように、PPG 内の表示方法を工夫して、使いやすさと小ささを上手いバランスで共存させられる UI 作りに精を出します。


でも、PPG での表示調整は限界がありますし、そもそも PPG の中の見え方を調整できるというだけであり、PPGウインドウそのものを小さくすることはできません。 PPG を表示させるコマンドである InspectObj には、フローティングウインドウの大きさ調整機能が無いからです。↓

InspectObj (2012)
 http://download.autodesk.com/global/docs/softimage2012/en_us/sdkguide/index.html?url=si_cmds/InspectObj.html,topicNumber=si_cmds_InspectObj_html




実は、モーダルPPG のみ、2012以降はサイズが変更できるようですね。今度試してみよう。↓

PPGLayout.SetViewSize (2012)
http://download.autodesk.com/global/docs/softimage2012/en_us/sdkguide/index.html?url=si_om/PPGLayout.SetViewSize.html,topicNumber=si_om_PPGLayout_SetViewSize_html





そこで、InspectObj で PPG を表示させるのではなく、View を新規に作成してその中にプロパティを埋め込むという方法を採ります。 なぜならば、View オブジェクトにはウインドウの大きさを変える機能があるからです。


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

var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "はげちょびん";
oP.AddParameter2( "bHage", siBool, true );

var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "はげちょびん";
oP.AddParameter2( "bHage", siBool, true );

var oL = oP.PPGLayout;
oL.AddItem( "bHage", "はげ" );

//InspectObj( oP );

var oActiveLayout = Application.Desktop.ActiveLayout;
var oNewView = oActiveLayout.CreateView( "Property Panel", "はげちょびん" );
//    この時点でまだ空っぽの View
oNewView.BeginEdit();   
//    この Begin と end が必要な理由が俺よくわかってない
    oNewView.Resize( 100, 55 );   
//    ウインドウの大きさ
    oNewView.Move( 300, 400 );   
//    ウインドウの画面上の出現位置
    oNewView.SetAttributeValue( "targetcontent", oP.fullname );
//    ここで初めて、View の中身が決定される
oNewView.EndEdit();

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


View

画像の左側が InspectObj でやった場合のウインドウです。 右が、View を使って小さいウインドウにした場合です。 スクリプトの途中に  //InspectObj( oP ); という部分がありますが、ここを非コメント化して、逆にそれ以降の行を全部コメント化すると、InspectObj バージョンになります。

View を使った方が、あからさまに小さいです。当たり前です。小さくしたんですから。 InspectObj はこれができません。 これがやりたかったんです。





このスクリプトの場合、oP にプロパティが格納されているわけですが、そもそもプロパティとは、シーンの中に埋め込まない限り、いわば概念上の存在です。実体の無い仮想的な入れ物とでも言うか。 それを視覚化するために InspectObj コマンドや CreateView メソッドを使います。 今回のプロパティはシーンに埋め込まず、CreateObject を使って仮想的に作っているので、これらを使わないことには、スクリプトからの制御は出来ても、ユーザにそれを見せることができないわけです。

しかしそれをユーザに見せるためにいつものように InspectObj コマンドを使ってしまうと、表示サイズの制御ができないので、サイズ調整機能を持った View オブジェクトを作りましょうというのが、この記事の話です。



まず、Viewオブジェクトを作るための場所を指定します。


  var oActiveLayout = Application.Desktop.ActiveLayout;


現在アクティブになっている XSI の Layout を指定します。 この Layout オブジェクトに対して CreateView メソッドを使うと、新しい View が作られます。 こうです。


  //    この時点でまだ空っぽの View
  var oNewView = oActiveLayout.CreateView( "Property Panel", "はげちょびん" );


「はげちょびん」 ってのはただの名前なのでどうでもいいです。 大事なのは最初の引数です。 プロパティを表示させたい場合は最初の引数で "Property Panel" と指定してやります。 なぜかはわかりません。そういうもんです。 

ちなみにここで "Explorer" と指定すると、Explorer が新規で出現します。 "Render Tree" と指定すれば、Render Tree が新規で出現します。 キーボードショートカットの 78 などを押した場合と同じ挙動です。 今回の場合はプロパティを出現させたいので、"Proeprty Panel" にします。 これはそういうもんだと覚えてしまうしかないかな。


この時点ではまだ空っぽの View を作っただけです。 "Property Panel" だよと宣言したことによってプロパティが埋め込まれる準備はできたのですが、まだ肝心のプロパティそのものは埋め込んでいません。 でもこの時点で実行させても、既にCreateView メソッドを実行している以上、View は出現します。 空っぽだというだけです。


プロパティを埋め込むのと順番の前後はどうでもいいのですが、サイズと位置をここで決めています。


    oNewView.Resize( 100, 55 );    //    ウインドウの大きさ
    oNewView.Move( 300, 400 );    //    ウインドウの画面上の出現位置


さっき作られた空っぽの View オブジェクトは、oNewView に格納されてます。 oNewView に対して Resize や Move のメソッドで数値をくれてやれば、望みの大きさと位置になります。 今回の場合、主にこの Resizeメソッドを使いたいがために、InspectObjコマンドを避けてわざわざこんな面倒なことをしているわけです。


で、最後に(先にでもいいんですが)、


  //    ここで初めて、View の中身が決定される

  oNewView.SetAttributeValue( "targetcontent", oP.fullname );


ここで、やっとプロパティを View に埋め込みました。 第1引数の targetcontent ってのが、View の中身をセットするためのアトリビュートの名前です。 中身自体は、第2引数です。 今回、前半で CreateObject で仮想的に作ってあるプロパティ = oP を与えてあります。Fullname を渡すということになっています。

順番はどうでもいいと書きましたがもちろん、oNewView に新しい View オブジェクトを格納する前に targetcontent がどうしたとか言ってもダメです。 ちゃんと View オブジェクトをゲットした後であれば、中身をセットするのと、Resize や Move する順番は、テレコになっても構わないという意味です。



作り方は以上。








この方法を採ったときの問題点。

タイトルバーをダブルクリックで畳んでしまうと、再びダブルクリックで戻した時に、空っぽになってしまう。  

Viewempty

なぜ? これはかなり嫌ですね。 空っぽにしない方法があるでしょうか。 どなたかご存知でしたら教えて下さい。





プロパティのパラメータを Preset として保存/読み込みできない。 これは、InspectObj で PPG を表示させたときは右上に出てくる Load/Save Preset が View には無いからですね。 

Loadsavepreset

こればっかりは仕方ない。
どうしても View で Load/Save の機能を付けたかったら、スクリプティングで Preset の Load/Save は可能なので、自前でボタンでも付けて Load/Save させるしかないでしょう。 ただ、そんなことしてボタンに UI 面積を取られて大きくなっていくと、View を使って小さくしようとしたという当初の目的に反するかもしれません。 あるいは View で Load/Save ができる他の方法があればどうか教えて下さい。





以上。






ちなみにプロパティを表示させるために View を使う理由は必ずしも小さい UI が欲しいからというわけでもなく、Move メソッドを使って位置を指定できるのも大きなメリットだと思うんですよね。 特に、多くのプロパティをズラズラと並べて表示させながらいじりたい時などに使えると思います。


こういうのとか。↓

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

var aString = Array( "は", "げ", "ち", "ょ", "び", "ん" );
XX = 100;
YY = 100;

for ( var j=0; j<10; j++ )
{
    for ( var i=0; i<6; i++ )
    {
        var oP = XSIFactory.CreateObject( "CustomProperty" );
        Num = i+1;
        oP.name = "はげちょびん " + Num;
        oP.AddParameter2( "sHage" + Num , siString, aString[i] );

        var oL = oP.PPGLayout;
        var oItem = oL.AddItem( "sHage" + Num, "はげちょびん文字" );
        oItem.SetAttribute( siUICX, 100 );
       
        var oNewView = oActiveLayout.CreateView( "Property Panel", aString[i] );
        oNewView.BeginEdit();
            oNewView.Resize( 250, 60 );
            oNewView.Move( XX, YY );
            oNewView.SetAttributeValue( "targetcontent", oP.fullname );
        oNewView.EndEdit();
       
        XX += 250;
    }
    XX = 100;
    YY += 80;
}


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

楽しいことになるので、コピペして実行させてみて下さいよ。

けけけけけけけ。


その後は、インターフェース上のメインメニュー Window > Close All でお願いします。けけけ。


同じ操作をスクリプトで書くなら、

JScript
----------------------------------------------------------------------
var oViews =  Application.Desktop.ActiveLayout.Views;
for ( var i=0; i<oViews.count; i++ )
{
    oViews(i).State = siClosed;
}

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

これでもいいですね。 実行させると、スクリプトエディタそのものも閉じられちゃうので一瞬焦りますが。

このように、View オブジェクトの State プロパティを使えば最小化とか元に戻すとかも出来ますね。 このやり方は空調屋のおじさまに聞きました。  詳しくはマニュアルの View オブジェクトページから State プロパティをたどってみて下さい。




あとは、Python なんかだと、マウスポインタの座標もゲットできるんじゃなかったでしたっけ? だとすると、マウスの下にプロパティを View で表示させるとか、できるんじゃないでしょうか。  JScript でもできるかなあ?  っていうか、マウスの座標の取得とかって、XSI の SDK側でその機能を持たせられないでしょうかね?  画面の解像度とかも。







マーティンも View でやっています。
http://myara.blog.fc2.com/blog-entry-124.html


あ、View云々とは関係ないですが、このハードエッジツール、死ぬほど便利です。  普通のやり方=右クリックして Mark Hard Edges なんて、もうやってられません。 非常にジェネリックで汎用性の高い、万人におすすめ出来るツールですこれは。








.

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

2012年6月12日 (火)

取得 その1001。 全ICETree/RenderTreeビューの取得。

選択中の ICETree ノードに対してごにょごにょするスクリプト、というのが最終的には目的なんですが、今回はその前提の段階となる、選択中のノードの取得について最近やったことを書きます。 あ、違った、更にもう一段階前の話です。 「選択中」のノードと言うけれど、じゃあ、いったいどこで選択中なのですか? という話です。


どこで選択中なのか? って、ICETree で選択中に決まってるじゃないですか。でも、ICETree って言うけれど、ICETree にはフローティングウインドウもあれば、ドックされた状態(レイアウトに埋め込んだ状態)もあるじゃないですか。それらを網羅して全部取得できないかしら、という実験の途中経過という感じです。




実はですね、この SI-Support の記事です。 いつもお世話になっているこのブログの、選択中の ICEノードをゲットするという記事を見て、やってみたんですよ。 そしたら、フローティングウインドウは問題ないけれど、ドックされたビューの中にある ICETree ビューには、その記事のスクリプトが効かないんですよ。 ドック状態のICETree ビューは無視されちゃうんです。 あ、ここで言うドック状態とは、フローティングではないという意味で言っています。 例えばビューポートの4つのビューのうち、ビューポートCを横に広げてビュー2つ分を占拠して使ったりするじゃないですか。そういう状態のことです。



その記事の中では、フローティングの ICETree ビューは、ビューの名前が ICE Tree だからいいけど、フローティングじゃないと名前が違ってしまうために、名前で選り分けることができない。 だから Type でフィルタしなさいという意味のことが書かれています。

しかしですよ、この記事のスクリプトを試してみると、選り分け対象にするために最初に全部取得するビューの中に、ドックされたビューが入っていないんです。 フローティングしか取得されないんです。 だから、Type でフィルタするとか以前の問題です。 ドックされた ICETree ビューは取得できておらず、取得できてない中から探しても当然見つからないわけです。意味ないです。


ですよね?

この記事の方法、フローティングウインドウにしか効きませんよね?

俺だけ?

誰かやった人は、教えてください。







そこで、こんなスクリプトを書いてみました。





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


//    どちらかをコメントアウト
TargetType = "ICE Tree";

//TargetType = "Render Tree";


Logmessage( "----------------------" );



//    ファンクションに飛ばして目的のビュー(ICETree か RenderTree)を全部引っかき集める
var oMyFuckinViews = GetFuckinViewsByFuckinType( TargetType );


Logmessage( "ヒットしたView の数 :" + oMyFuckinViews.count + " (" + TargetType + ")" );
Logmessage( "----------------------" );

//選択されていたノードの格納容器
var oNodes = XSIFactory.CreateObject( "XSI.Collection" );
oNodes.Unique = true;   
//これがないと、1つのICETreeを複数ウインドウで開いてた場合、ノードがダブる

//親オブジェクトの格納容器
var oNodeParents = XSIFactory.CreateObject( "XSI.Collection" );
oNodeParents.Unique = true;
//これがないと、選んでいたノードの数だけ親がいることになってしまう

//    ファンクションに飛ばして引っかき集めた View 全部をループ
for ( var i=0; i<oMyFuckinViews.count; i++ )
{
    var oMyFuckinView = oMyFuckinViews(i);
    Logmessage( "ヒットしたView(" + (i+1) + ") - " + oMyFuckinView );
   
   
//    選択中のノードをカンマ区切りの文字列として取得
    StringSelected = oMyFuckinView.GetAttributeValue( "selection" );
    var aSelected = StringSelected.split( "," );//    それをアレイに変換
   
    for ( var j=0; j<aSelected.length; j++ )
//    アレイをループ
    {
        if ( aSelected[j] != "" )
//    何もノードを選択してない場合、なぜか無文字の部屋ができてしまうので除外
        {
            var oNode = Dictionary.GetObject( aSelected[j], false );
//    文字からオブジェクトとして取得
            if ( oNode )
            {
                oNodes.Add( oNode );
//    オブジェクトとしてノードを取得できたら、格納
                Logmessage( "   そのViewで選択中のノード(" + (j+1) + ") - " + oNode.Name );
                if ( TargetType == "ICE Tree" )/
/    親も格納  ICETree の場合はその親である3Dオブジェクト
                {
                    var oParent = oNode.Parent3DObject;
                }
                else if (  TargetType == "Render Tree"
)//    親も格納  RenderTree の場合は単純にノードの Parent
                {
                    var oParent = oNode.Parent;
                }
                oNodeParents.Add( oParent );
                Logmessage( "      そのノードの親 : " + oParent.Name );
            }
        }
    }
}

Logmessage( "----------------------" );
Logmessage( "選択されていたノードの数 : " + oNodes.count );
Logmessage( "選択されていたノードの親オブジェクトの数 : " + oNodeParents.count );




//    View のタイプを文字列で渡すと View が格納されたコレクションを返すファンクション
function GetFuckinViewsByFuckinType( TargetViewType )
{
   
//    格納容器
    var oTargetViews = XSIFactory.CreateObject( "XSI.Collection" );

   
//    フローティングかどうかを問わず全View を ViewCollection として取得
    var oViews =  Application.Desktop.ActiveLayout.Views;
    Logmessage( "全ビューの数 :" + oViews.count );

   
//    取得した全View をループ
    for ( var i=0; i<oViews.count; i++ )
    {
        var oView = oViews(i);

  Logmessage( oView + " : " + oView.type );//これを非コメント化で詳細ログ
       
       
//    Type が指定のものと一致したら、格納(ここでヒットするのは、フローティングのもの)
        if ( oView.type == TargetViewType )    oTargetViews.Add( oView );
       

        //    View.Views プロパティは、効かない View オブジェクトにはエラーが出るので、仕方なく try catch
        //    なんかいい方法ないですかね

        try{
            var oViewViews = oView.Views;
            for ( var j=0; j<oViewViews.count; j++ )
            {
                var oViewView = oViewViews(j);
               
//Logmessage( "   ---- " + oViewViews(j).type );//これを非コメント化で詳細ログ
               
               
//    Type が指定のものと一致したら、格納(ここでヒットするのは、ドックされたもの)
                if ( oViewView.type == TargetViewType )    oTargetViews.Add( oViewView );
            }
        }
        catch(e){}
    }
   
    return oTargetViews;
}

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

このスクリプトのダウンロード(右クリックで保存)




実行すると、このようにスクリプトエディタの中に結果がログされます。

Geticetreeview

このスクリーンショットでは、ドックされた ICETreeビュー(ビューポートC)と、フローティングの ICETree ビューの2つがありますが、ログを見るとその両方がヒットしています。


どうでしょか。フローティングウインドウも、ドックされたビューも、全部取得して、ICETree だけ抽出できているように見えるんですがね。 どうですか。

ちなみに RenderTree にも効くようです。スクリプト冒頭で、TargetType という変数の中身を "Render Tree" にするだけです。






大事なのはサブルーチン(ファンクション)の中身の方なので、そちらだけ重点的に書いておきます。 スクリプト後半にあるファンクション GetFuckinViewsByFuckinType の中身です。



マニュアルを調べたり、過去によくわからないまま書いた自作スクリプトの残骸の調査から、フローティングかドックされているかに関係なく現在作業中の XSI 上で開かれている全部の View を取得するには、以下のように書けばいいらしいと判りました。


   
//    フローティングかどうかを問わず全View を ViewCollection として取得
    var oViews =  Application.Desktop.ActiveLayout.Views;
    Logmessage( "全ビューの数 :" + oViews.count );



これで oViews にビューがコレクションとして入りました。例えば自前のカスタムシェルフなどを開いていた場合、それもここで取得できています。 その他、インターフェース上に見えている全てのものが取得できているように見えます。


で、フローティングウインドウの ICETree の場合は、ここで取得できた View オブジェクトというオブジェクトの Type を調べると、もう ICE Tree になっているんですよ。 なので、取得できた全ビューをループして、ひとつずつ Type を調べます。


 //    取得した全View をループ
    for ( var i=0; i<oViews.count; i++ )
    {
        var oView = oViews(i);
      
//Logmessage( oView + " : " + oView.type );//これを非コメント化で詳細ログ

    //    Type が指定のものと一致したら、格納(ここでヒットするのは、フローティングのもの)
        if ( oView.type == TargetViewType )    oTargetViews.Add( oView );



Logmessage の行は、表示が長くなるのでコメントアウトしてますが、頭の // を削除して非コメント化すれば詳細がログされるので何が取得できているのかよくわかると思います。

上の SI-Support の記事のように Filter を使っても良かったんですが、なんとなくこちらの方が分かりやすかろうと思って、ループして Type をひとつずつ調べる方式にしています。

で、ここまでは本質的には SI-Support の記事と全く同じです。 違うのはここ以降です。







例えば、上に書いたようにビューポートCを ICETree にしていた場合にどうなるか。


4つのビューポート A,B,C,D って、実は View Manager(内部的な名前は vm ) というものの下に階層化されているんですよね。

なので、上記の  Application.Desktop.ActiveLayout.Views で取得できるのは、この vm までなんです。 取得できた View の Type を調べると、"View Manager" って出てきます。Logmessage を非コメント化して詳細ログを見るとわかります。

Viewviews

Type を調べたら View Manager であって ICE Tree ではないのだから、当然上の Type 判定には引っかかりません。 よって、ビューポートCは ICETree にしてあるにもかかわらずスルーされてしまうのです。 これが、SI-Support のスクリプトの問題なような気がしたのです。



なので、取得できた View オブジェクトに対しもう1回 Views プロパティを使ってその下層にある View を取得します。


        //    View.Views プロパティは、効かない View オブジェクトにはエラーが出るので、仕方なく try catch
        //    なんかいい方法ないですかね

        try{
            var oViewViews = oView.Views;
            for ( var j=0; j<oViewViews.count; j++ )
            {
                var oViewView = oViewViews(j);
               
//Logmessage( "   ---- " + oViewViews(j).type );//これを非コメント化で詳細ログ

 //    Type が指定のものと一致したら、格納(ここでヒットするのは、ドックされたもの)
                if ( oViewView.type == TargetViewType )    oTargetViews.Add( oViewView );
            }
        }
        catch(e){}


これです。


ここでわかりにくいのは、Views プロパティって言っても実は2つあることなんですよね。 

ひとつは、上の


  var oViews =  Application.Desktop.ActiveLayout.Views;


これですね。 Layout オブジェクトの Views プロパティです。 これで取得できるのは View オブジェクトコレクションです。 ひとつひとつを取り出せば、それは View オブジェクトです。

でも、そうやってひとつひとつ取り出された View オブジェクトにも、さらに Views プロパティがあるんですね。 今度は View オブジェクトの Views プロパティです。 これで取得できるのはやはり、View オブジェクトコレクションです。ひとつひとつを取り出せば、それは View オブジェクトです。


このように、View オブジェクトは Layout オブジェクトのすぐ下層にある場合もあるし、Layoutオブジェクトのすぐ下層にある View の更に下層にある場合もあるという、全くもって腹の立つ分かり難さです。このために  Layout.ViewsView.Views を両方試してあげないと、全部の View が取得できないことになります。



もっと厳密に言うと、View.Views で得られた View それぞれにも Views プロパティがあるわけで、これは再帰的無限地獄に突入する気がします。 なのでこのスクリプトでは1階層しか潜っていません。




とまあそんなわけで、Layout.Views から取れた View そのものの Type が ICE Tree ならそれはフローティングのはずだし、ドックされていれば vm などの子供になってるはずなので一階層潜って( vm の Views を調べて)更に Viewオブジェクトを取得し、そちらに対しても Type のテストをしてあげるということをやっているのが、このファンクションです。

ちなみに、vm 以外で同じ状態になる例を挙げると、自作のカスタムシェルフ、リレーショナルビュー等だと思います。 あと、XSI Explorer なんかもビューの子供にビューがあるタイプのビューなので、フローティングの XSI Explorer の中に ICETree が含まれる場合などは、同様に View.Views を調べてあげる(=フローティングの XSI Explorer (Viewオブジェクト)に対してさらに Views を調べてあげる) 必要があるかも知れません。







それともうひとつ、Layout.Views で取得できた View オブジェクトのうち、全ての View オブジェクトが、Views プロパティを持っているわけではない、という点も重要です。 上の vm の例のように、1階層潜ったところに ICE Tree が存在してないか調べようとして、取得できた全 View に対し Views プロパティをチェックしようとすると、エラーで止まってしまうのです。 

vm やカスタムシェルフやリレーショナルビューや XSI Explorer など、下層に何かのビューを所有している View オブジェクトに対しては効くのですが、Desktop.Views で得られる全 View のうち下層に何も持たないやつも多いので、そいつらに対して Views プロパティの呪文を唱えると、エラーで止まってしまうんですよね。



これを回避する方法がわからず、仕方なく try catch を使っています。 try catch ってのは全てのエラーに対して効いてしまうので、例えばただのタイプミスで動かないスクリプトでも、そこで止めずに実行を続けてしまいます。なのでメンテナンス上非常に都合悪い。問題箇所の特定が難しくなりがちです。 なのでなるべく使いたくないのですが、他に方法が見つからない時はこうして仕方なく使ってます。 なんか方法ないでしょうかね?








ともかく、これでドックされたビューの中にある ICE Tree ビューもちゃんと取得できるようになったように見えるのですが、どうでしょうかね。落とし穴ありますかね。 どなたか、もし試してみたら、教えて下さいませ。

っていうかそもそも、もっと簡単に全 ICETree ビューを取得する方法、あるのでしょか?  あったら知りたいです。







.

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

2012年6月 8日 (金)

頂点上にパーティクルを発生させる。 その4。

さらに続き。


m4g さんにはいつもお世話になっております。


パーティクルをブツの頂点上に発生させて、そして法線方向に向ける、というものですが、m4g さんに御教示頂いたツリーを載せておきます。 この方法の方が良い気がする。


この記事では、法線方向に向けるということのみに注目していますので、それ以外の要素のことは敢えて省いてあります。

Pointatnormal_m4g

クリックするとでかい画像。


このツリーで、パーティクルの Orientation を決定する(法線方向に向ける)ために使っているのは、Increment Rotation with 2 Vectors という標準コンパウンドです。 画像の中のEです。 (ちなみに、俺が前回使ったのは Rotate Vector でした)

この Increment Rotation with 2 Vectors ってのは、ある回転値(Rotation)をベースにして、ローカルのこのベクタ(Local Vector)を、ワールド座標のこの位置(To Vector)に向けるために必要なだけの回転値をベース回転値に加算するぜ、というコンパウンドだと思います。マニュアルにそう書いてあるように見えます。 しかし、例によってマニュアルの表現って不思議ちゃんな感じですね。


今は cone をパーティクルの形状に使っていて、とんがった方向をパーティクル発生源メッシュの法線ベクタに向けたいので、これはパーティクルのローカルY方向ですね。

ということで、Increment Rotation with 2 Vectors に食わせる情報は、以下のようになります。

  Rotation = パーティクル自分自身の元の回転値(現時点でゼロですが)。

  Local Vector = 0, 1, 0 (パーティクルのY方向)

  To Vector = パーティクルを発生させたメッシュの法線ベクタ



Rotation は、自分自身のローテーションですから、self.Orientation をゲットすればいいだけなので問題なし。今回は Get Particle Orientation コンパウンドを使っています。


Local Vector はそのまんま、0, 1, 0 を入力します。



問題は To Vector です。 というのは、こいつは発生源メッシュから法線情報をもわらねばなりません。 

俺ごときは画像内のDのように普通に発生源メッシュの PointNormal を突っ込めばいいやと考えてしまいがちなのですが、これはコンテクスト違いでつながってくれません。
 
Pointatnormal_m4g_unabletoconnect

なぜつながってくれないのか。 

それは、Increment Rotation with 2 Vectors は自分自身(PointCloud)の情報を欲しがっているのに、他人(発生源メッシュ)の情報を突っ込もうとしているから、だと解釈しましたが、合ってますかね。



つまりですね、そもそもこの ICETree が与えられているオブジェクトは発生源メッシュではなく PointCloud なのであり、その PointCloud に属するパーティクルの向きを決めるために Increment Rotation with 2 Vectors を使っているんだから、こいつは自分自身の情報(PointCloud が所有する情報)しか食ってくれない、という意味だと思うんですよね。


そこで、発生源メッシュが持っている情報を PointCloud 自身の情報にコンバートしてやらねばならない。 そのために、Get Closest Location ノードを使っていると考えられます。 画像内のBです。

Pointatnormal_m4g_2
クリックするとでかい画像。

発生源メッシュから Geometry ポートに情報を渡してます。 次にAではパーティクル自身のポイント位置を拾ってきています。 そして先ほどの Geometry とAを比較して一番近い場所(Location)を吐き出しています。 「A(自分自身の情報)のうち、メッシュに最も近いところを吐き出す」ですから、吐き出すものは自分自身の情報であるという部分が重要です。 そしてこのとき、パーティクルはもともと発生源メッシュの頂点位置に発生させているわけですから、「一番近い位置」 は頂点とズバリ 「同じ位置」 であると保証されています。 

そしてその位置を、Cの Get .PointNormal に食わせているので、CはBからもらった位置における法線方向を吐き出すということになります。 Bからもらった位置とは、自分自身の情報であるという部分が重要です。 

結果、パーティクルが発生しているズバリ同じ位置における、発生源メッシュの法線方向(他人様の情報)を、自分自身(PointCloud自身)の情報としてゲット出来たのがCだということになります。 よね? 合ってますよね?  で、もう自分自身の情報なんだから、自分自身のローテーションを決めるためのEに食わせることが可能になった、という解釈です。 


別の言い方で言うと、Get Closest Location の Position ポートに入っているのはAで取得した自分自身のパーティクル位置ですから、Bが吐き出すのはあくまでも自分自身が持つ情報だということです。

その自分自身が持つ位置情報のうち、発生源メッシュに一番近い場所=実はズバリ同じ場所をBは吐き出しているわけで、実質的には発生源メッシュの情報を100%コピーしたことになりますね。 コピー元は他人様で、コピー先は自分自身。 以降はコピー先(自分自身の情報)を利用する。 という感じ。 




ということだと思っています。
冗長ですみません。
しっかり説明できるくらい理解したい。
ちゃんと理解できたら、もっと簡潔に書けるかな。。。。
間違いあればどうか指摘お願いします。


ええと、前回までのツリーでは Build Array from Set を使っていたのですが、なぜアレイが必要なのかよくわかってませんでした。 他人様から情報を頂く時はアレイが必要なのかな? なぜ? みたいに思っていました。

でも、もし今回と同じ考え方ができるとすれば、アレイを介すことによって他人様の情報が自分の情報になる、と解釈できる気がしました。 アレイの入力は他人様からもらってるけど、アレイそのものはPointCloud 自身の ICETree 内に発生させているものなので、アレイが吐き出す結果はもう自分自身が所有する情報である。自分自身の情報になったのであれば、自分自身の情報を要求する下流ノードにそのまま接続することができる。 ということなのではないか。

どうですか。間違ってますか。どうか指摘して下さい。







もうひとつ重要な発見、というか新たな謎が・・・。


前々回の記事の後半で、ベースメッシュを変形させると、ビューポートが表示する法線=青い線と ICETree が返す法線が一致しないと書きました(GIFアニメーションになってます)。

しかし、今回 m4g さんに教えてもらった Get Closest Location 方式で取得した法線は、変形後も、ビューポートが表示する青い法線と完璧に一致するのです。

Pointatnormal_m4g_matchnormal

前回のツリー、Rotate Vector 方式では一致せず、今回の方式で一致するのは、うーむ、なぜなのでしょう。


一致したからどうだというものでもないのですが、その仕組みを説明できるほどは、俺はまだ理解できていません。 どなたか説明してくれませんか。 そこらへんが解ると、さらに理解が進みそうな悪寒がします。







.

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

2012年6月 7日 (木)

頂点上にパーティクルを発生させる。 その3。

また少し改造してみました。


前回までのは、パーティクルの cone がベースメッシュに埋まっていたんですよね。 というのは、パーティクルShape の cone は、センターの位置がコーンの底面ではなくブツの中心であるため、そのセンター位置で頂点上に配置されてしまうため、cone の底面付近が埋まってしまうという状態になっていました。


まあ、今回 cone を使っているのはただの実験用ダミーとしてなわけですが、実際に使うときは当然、ベースメッシュの頂点位置にガチガチに固定されてしまうのではなく、任意にオフセットできた方が便利だと思います。 なのでその方法を考えました。  とか言って、全然大したアレではないですよ。 メモですメモ。

Offsetparticlesalongnormal_2

ビューポートを見ると、コーンが法線方向に浮いているのがわかると思います。 それぞれの頂点位置における法線方向にオフセットしたからです。



追加されたのはコメントで囲ってある部分です。それ以外は、前回と全く同じツリーです。




でオフセット量を決めています。XYZ ですが、今回は Y だけオフセットしてます。 X と Z にもゼロ以外の値を入れれば同じようにオフセットされるはずです。はず。


ベクタですので、オフセット量だけでなくオフセットする方向も司っているのですが、そのままだとグローバル座標での方向になりますので、ベースメッシュの SRT に追従するよう、ベクタを回転させてやらねばなりません。どんだけ回転するかは、パーティクルを法線方向に向かせるツリーを組んだ時点で得られているため、それを再利用してます。 これで は正しい方向を向きました。


あとは元のパーティクルのポジションに、新しく得られたオフセットベクタを足してやればいいだけです。たぶん。 なので で元のパーティクルポジションを取得し、 の Add ノードで の結果に加算します。


その結果を、 でパーティクルにセットし直してあげて終了。




合ってますかね? この説明で合ってますかね?  どうかツッコミお願いします。 
 

そもそも、前回まで組んだツリーはそのままで良いのだということを前提に進めてしまっています。 どんな落とし穴があるのかもわからないまま。 

まあいいや、俺は勝手に勉強になってます。 全力でメモ。覚えているうちにメモ。ホヤホヤのうちにメモ。そのモチベーションがあるうちにメモ。どうせすぐ萎えて終わるんだからやる気のある時だけメモ。





最初、上手く行かなかったんです。
というのは、Init Particle Data でパーティクルの初期状態(向きも含む)を決めてあげる時に、いっぺんにオフセットもやろうとしていた感じなんですよ。今思えばですけどね。
それでああでもないこうでもないとやって、上手く行かなくて。

で、ふとよくある ICE のデータのセット方法のパターンが思いついて。 当たり前なんですが、一度セットし、それをゲットして、加工した後に戻してあげるという。 カスタムアトリビュートに格納し、それを Get Data してゴニョゴニョした後に Set Data とか、よくやるじゃないですか。

ってことで、画像の黄色印の部分ですが、AddPoint 内の最初の OnCreation にブチ込んだツリーで初期状態を決め、その初期状態をゲットしてきて、オフセットをかましたのちにセットし直してあげる、という考え方でやってみたら上手く行ったように見えます。


この辺の発想に至るかどうかあたりが、今の俺のレベルでは、重要な分かれ道になる気がします。 いっぺんにやろうとせず、一度セットして、その後ゲットし、加工した後にもう一度セットし直して終了。  ううむ、体に染み付くといいのですが。。。。






.

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

2012年6月 6日 (水)

頂点上にパーティクルを発生させる。 その2。

前回の続きで、まずはパーティクルの方向を法線方向に向かせたかったので、なんとか自力でやってみたら、こんなになりました。



よくわからないままやっているので、頓珍漢だったり、無駄があったりしたら、どうか指摘して下さいませ m(_ _)m

Normal1b_2
クリックするとでかい画像。

早く他の人の植林系コンパウンドを見ればいいんだけど、そこで膨大なエネルギーを使う前に自前であがいておいた方が「正解」を見たときに理解も深まるであろう、などという理屈は言い訳です調べるのめんどくさいだけですすいません orz




一応、やりたいことはできているように見える。 オブジェクトを動かしても、回転やスケールをしても、どんな親子関係にあっても、パーティクルは常に頂点位置に追従し、かつ、頂点の法線方向に向かっているように見えます。

でもどんな場合でもその関係が崩れないのかの検証はもちろんまだ浅過ぎて、これで良いとは断言できません。 と言うより、あてずっぽうでやってみたらとりあえずの結果はOKに見える、というのが本当のところです。


まず疑問なのは、Aの流れでは、MAN の PointPosition をそのまんま AddPoint に突っ込めるのに、法線方向を取得する部分 PointNormal では、Bのように一度アレイを構築してやらねばならないように見えることです。 

A1 から AddPoint に直でつなげます。
A2 からも、画像にあるように可能です。 

PointPosition なら直でぶち込むことが可能で、PointNormal だと、直接つなぐのはダメということ? 

いや、それは何に突っ込むかによって変わるんですよね?  ううむ、どういう場合は直でOKで、どういう場合はアレイが要るのか、あるいはその背後にあるロジックをちゃんと理解していれば、こんなどうでもいい部分でつまずくこともなくなりそうです。

そもそも、「アレイを構築すればOK」という観察結果そのものに確証があるわけでもなんでもありません。 「それまでコンテクスト違いでつながらなかったのに、以前に教えてもらったツリーを参考に意味もわからず真似してみたら、つながっちゃった」 というだけ なんですよね。  つまり、背後にあるロジックを分かってないだけでなく、議論の前提としている「アレイを構築すればOK」がそもそも正しいのかどうかすら、分かってないのです。 すいません。 どなたかスカッと上手く説明してくれないかなあ。



あと、Cの部分、Direction to Rotation を使う妥当性というか理由も、あまり上手く言えません。 なんとなくこれを選んでつないでみたら法線方向に向いてくれた、というだけです。 後付けでもいいからその理由を解説できれば理解につながるのですが、残念ながらしどろもどろ・・・・。 そしてそのパラメータである UpVector は、デフォルト値のまま放置しています。どういじってもコーンのY方向(尖った方向)は変わらず、ロールだけが変わるように見えます。とりあえず深く考えずに放置しています。







それともうひとつ、法線方向なのですが、ビューポートの目ん玉アイコンで Normals をオンにすると青い線で法線が見えますよね。 その青い線の方向と、ICE が返す PointNormal の方向が、メッシュ変形後にズレているのは何故なのか。そこが疑問でした。

Normal

まず PointNormal を Show Value して ICE が返す法線方向を可視化します(図の赤印)。  すると緑色の矢印で法線方向がわかります。 メッシュをいじる前は、青い線と一致しています。


しかし、メッシュを変形させた後は、なぜかこの青い線と一致しません。ズレています。 パーティクルであるコーンは、緑色の矢印=つまり ICE が返す PointNormal の方と一致していますね。 まあ、ICE の法線情報を使って向きを決定しているのだから、当たり前です。



ともかく、なぜこの不一致が起きるのか、よくわかりませんでした。 不一致だからと言って何か不都合があるのかと言えばそうでもないんですけどね。 でもまあ、ICE が示す法線方向と、ICE とは別でインプリされている法線表示機能が示す方向にズレがあるのは、なんとも気持ち悪いものです。 っていうか、その仕組みを理解できていない自分が気持ち悪いということです。


ちなみに、メッシュを変形させた履歴が残っているせいで、変形前の Normal を拾ってしまっているのかしら? とか思ってメッシュを Freeze して MoveComponent オペレータが残ってない状態にしてみましたが、やはりズレたままなんですよね。




そもそも法線というものがどういう仕組みで計算されているのか、もっと言えばそもそも法線とはなんぞや、ということを俺は明快に説明できません。 つまり、根本の理解が欠如したまま表層だけでなんとかしようとしていることになります。それがいかんのかも知れぬ。

でも、まあ、あまり求道的なCGを目指してもなあ。。。。 俺はCGを生業にしているわけであって、CG道を歩いているわけではないからなあ。



とかなんとか。

そんなこたどーでもいいですねすいません。

ICE をもうちっと気持ちよく乗りこなしたいって思ってるだけでーす

life goes on.....




.

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

頂点上にパーティクルを発生させる。

今さらこんなレベルの低い ICE やっててほんとすいません。

マトモに ICE をいじる仕事が半年以上もなくて、
全部忘れたり、そもそも最初から調べてなかったりして、
いざ何かをやろうとした時に、自分があまりにも何も知らなくて愕然とします。



ともかく、オブジェクトの頂点上に1個ずつパーティクルを発生させる ICETree をメモしておきたかっただけです。 昨日それをやろうとしたら、パパっと ICETree を作れずに焦りました。 なんでこんなことすら俺はできねえんだ、って。

Ichu_particlesonpoints

男オブジェクトの頂点上に、 Add Point でパーティクルを発生させています。 頂点の座標に、オブジェクトの Matrix とやらを掛けてやることによって、オブジェクトの SRT に追従するようになります。たぶん。  パーティクルの形状は、見やすいように cone にしています。

こんなんで合ってますかね?
どなたか、お気が向いたら、どうかツッコミを入れて下さい。


ここからさらに疑問は、

 ・ 1頂点につき2個以上発生させるには

 ・ cone の向きを法線方向に向けるには

などです。 

法線方向は、PointNormal を取得して Set Particle Orientation などにゴニョゴニョすればいいと思ったのですが、パパっと一瞬ではできませんでした orz
コンテクスト違いでつながらなかった。
ひとまず保留。
Build Array from Set と Select in Array でゴニョゴニョが必要なのか。
そもそもゲットする情報は PointNormal で合っているのか。
分かるお方、どうか教えて下さい。
教えて君ですいません。
自分でももちろん調べます。


思えばこの題材は、ICE植林というか、ICEでスキャッタリングをやる基礎みたいなものでしょうかね?  世の中には植林系の ICEコンパウンドも多くありそうなので、他の人のを参考にすればすぐ分かりそうですが、ちと、調べものばかりしてるわけにもいかず、仕事も進めねばならんし・・・・ と言い訳して保留するから進歩がねえんだよこのヴォゲ野郎ネットでくだらねえ動画見たり酒吞んだりしてる時間あったら調べものしろやこのハゲ野郎


それにしても、まさにこんなのは、半年くらい前に、
色んなお方に助けられてやったはずだと思うんだけど、
すぐにメモしておかなかったからなあ。。。
後で見るとか、後で調べるとか、ほんと、ダメだなあ。
でもすぐやる時間がなかったりするのも事実は事実ですよね。
ToDoメモだけ残して、一瞬のスキを突いて調べればいいんだろうけど、
その瞬間までモチベーションが続いていないのだと思う。
いかん。
いかんいかん。





今日もテイラーたんに懺悔しよう。



わあ、ストップモーション楽しそうですね。
激しく凝ったストップモーションではないと思うけど、
シンプルなのがむしろ良いな。
楽しそうなのは良いな。
やっぱり、作るのは楽しまないとねえ。





.

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

2012年6月 5日 (火)

FindObjects2。

FindObjects2 を使ったのは初めてかも知れない。 便利でした。

2011 からの新機能のようです。






↓現在開いているシーンの中にある全てのカスタムプロパティを引っかき集める

 var oProps = FindObjects2( siCustomPropertyID );




一発でかき集めることができました。 これは便利です。 siClassID を渡してあげるだけでいい。カスタムプロパティでも、カメラでも、Model でも、何でも。

今までもたくさん方法はあったけど、これが一番ラクなのではないかなあ。 例えば FindChildren というメソッドは前からありましたが、返って来るのが 3Dオブジェクト(X3DObjectCollection)限定なんですよね。 そのため、この例のようにカスタムプロパティを引っかき集めたいなどという場合には使えなかったんですよね。 この FindObjects2 は SIObjectCollection が返って来ますから、引数として渡してあげる ClassID で選別できるものは何でも取得できます。 Group でも Pass でも何でもです。 ClassID で取得してもまだ選り分ける必要があるような場合は、取得したコレクションをループしてひとつひとつの Type などをチェックしていくしかないでしょう。


ちなみに上のような書き方をすると一見コマンドように見えますが、、実は XSIApplication オブジェクトのメソッドなのですね。 なので正確には、

var oProps = Application.FindObjects2( siCustomPropertyID );



と書くのかな? 合ってますか?
Python では省略できないんだっけ? 
頓珍漢なこと言っていたら、どうか突っ込んで下さい。
というか教えて下さい。

ま、こういう場合は省略できてもあまり省略すべきじゃないかも知れませんね。 後で自分で読むときにわかり易いように。 フルで書かないと、1週間もするとすぐ忘れて意味不明になっちゃいますからね。







それにしても、今まで無かったのが不思議なくらいの基本的な機能ですよね。 

こういう基本機能が欠如していた場合、取得したいブツを選り分けるのはとても苦労します。 確実にユニークな特徴をひたすら探して、いっぱい条件分岐を付けて、ふるいを何段階もかけて、それでも選別できない時は例えば値を変えてみた時の振る舞いを観察するとか。 こうなるともはや邪教の呪文です。 値を変えたいわけじゃないのに、種類を判別するだけのために、一度値を変えちゃうという。 もちろん判別後はその値を元に戻してあげないといけないですね。 

あるべきプロパティやメソッドがないために無理やり変なコードを書いて実現することを、黒魔術と呼ぶようです。 黒魔術でしかできないことは黒魔術でやるしかないので、別に否定するものではありませんが、非常にメンテのしづらいスクリプトになるし、できれば避けたいですね。 

まあ、Type でもなんでもいいのでブツの種類によってなんらかのタグになるものがプロパティなどの形でインプリされていれば、最悪総当りループで調べられます。 そういう意味では XSI は、最低限必要なものは前からそろっていたというか、極悪な黒魔術は使わなくても済んでいたかもしれません。




FindObjectsコマンドというものは以前からありましたが、あれはいわゆる siClassID とは違う、 ClassID だか GUID だかという、ヒエログリフのような解読不能の長~い識別コードを渡してやらねばならず、これはこれでかなり黒い方法でした。
{76332571-D242-11d0-B69C-00AA003B3EA6} ← これ読んで、ああ、カスタムプロパティのことね、とかすぐわかるあなたは、黒魔術のミサに今すぐ参加して下さい。そして将来俺に話しかけることがあれば、どうか人間語でお願いします。




.

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

« 2012年5月 | トップページ | 2012年7月 »