« 2011年4月 | トップページ | 2011年6月 »

2011年5月

2011年5月30日 (月)

グッズでなつかしす。

家で大整理を敢行したら、色んなものが出てきやがりましてね。
長年使っていると、色んなもんをもらう機会があるんですねえ。



このバッグは全然記憶にない。
1
バッヂが付いていて、SIGGRAPH2009 と書いてあるので、ニューオーリンズでのSIGGRAPH 2009 の時におそらくは嘔吐デスクさんのブースでもらったんでしょうなあ。 ぺらぺらの生地の、実に品質の低いバッグです。 SIGGRAPH 会場でもらった資料を持ち帰るのに使ってね、ってくらいのものかな。




これもいつもらったのかは忘れた。
2
フード野郎がいるから6の時代か?と一瞬思うんだけど手から出ているのは ICE なアレなので、七ちゃんのラウンチイベントとかかしらね? いずれにせよ日本でもらったもののはず。



これは、SIGGRAPH2008 ですね。
3
上のウチワと同じ図柄ですね。 この時代は、嘔吐デスクさんですらなかったわけで。 ギリギリ Avid ですね。 SIGGRAPH で Softimage のブースでもらったはず。




これはどこでどうもらったんだか完全に忘れた。
4
筆記用具入れか、メガネ入れか。
ロゴの書体が Lithos 風のアレだし、きっとアビッド時代だと思うんですがね。




これも、なんなんだ・・・・?
5
どこでどうもらったかは知る由も無い。Softimage のタスキを付けている変な人形です。

顔が時計になってます。顔の部分はクリップになっていてメモなどを挟めます。足はフレキシブルに曲がります。 足の裏は磁石になっていて、冷蔵庫などに吸着します。

今日初めて開封して起動してみました。時刻を合わせて、1分後くらいにはなにやら挙動不審な動きをして、時計の時刻がリセットされてました。もう逝っちゃっていたようです。




これは・・・・・・
6
XSI まくら。

枕って言ってもちゃんとした枕じゃなくて、20センチ x 20センチくらいのサイズで、ちゃんとしたクッション材も入ってないショボい枕です。 ええと、なんだったかなあ、なんかのバージョンアップの時に、「今バージョンアップすると XSI 枕が付いてきます!!!」 とか宣伝していた気がするんですけどね。 ン十万もするソフトウェアのオマケがこのショボい枕かよ! と思った記憶があるんですが、どうだったですかね?

空調屋さんのロゴが入ってますね。ぴちょんくんタオル、ぴちょんくん扇子、ぴちょんくんボールペンなどはよく使ってますよ空調屋様。




これも忘れたなあ。
7
Softimage タオル。 明らかに Avid 時代だと思います。 たぶん。




わっ 食い物も出てきたっ
8
Softimage 金太郎飴!!!!

フード野郎がいるから、6の時代かな?  たしか日本のイベントに合わせて、上野だかどこだかの金太郎飴の老舗な店に発注したと某嘔吐デスクのお方(当時 Avid のお方)が言っていたような気がするんですが、忘れました。

2008年10月だかに、賞味期限が切れています。
9
よく見ると、XSI という文字や FR という文字や(おそらく Face Robot のこと)、フード男をデフォルメしたらしい図柄の飴もあります。 

1個も食っていません。袋の中でカオスなことになってます。永遠に開けません。 4つあるので、欲しいお方がいましたら、あげます。





これもどうもらったんだか忘れた。 なつかしい!
10
XSI マウスですね。 コードが硬くて使いにくいとか散々文句言いながら、けっこう使った記憶があります。小さいので、携帯用という感じです。 図柄から言うと、XSI 5 かな? 4 かな?





他にもいっぱいあるはずですが、今日の整理では出てきませんでした。
Tシャツなんかはいっぱいありますね。

以下は XSI と関係ありません。





ワコム。
11
インチュオスとかそんなん無い時代のペンタブですよ。
USB なんてものも、たぶん無かったかも。 シリアル接続です。

このペンタブは、オークションで買った気がする。 でもその前に初めて自分で買ったペンタブは、ワコムじゃなくて、なんちゃらというメーカーの、「ドローイングスレート2」だったはず。Macの ADB接続だったはず。 ワコムは硬かったんですよ。硬くて描きづらい。ドローイングスレートの方が柔らかくて、いい感じだったんです。 今はどうなったのだろうか。





さらに古いのが出てきた。
12
これはもう、 俺の原点。


1994年に買ったものです。
Apple のキーボードとマウス。
もちろん Macintosh 用、ADB 接続です。

しかも、インターネットなどというものが普及する前。
NIfty-Serve という、「パソコン通信」 の 「売ります買います」 的なやつで買ったんですよ。

同じく Nifty の売ります買いますで、我が人生初のCGがまともにできそうなコンピュータを買ったんです。 Apple の Macintosh Quadra840AV でした。中古で 320,000円で買いました。 今だと考えられねえ。32マソだって。で、その Macintosh 買うのと同時に、このキーボードとマウスを買ったんです。同じく中古で。 当時はPC本体とキーボードやマウスは別売りだったっけ? 忘れました。


このキーボード、キータッチがすんげえ重い。 バチッ バチッ という感じ。
ファンクションキーねえし。
マウスも重い。 動かすのに手が疲れる。
クリックも重い。 節度ありすぎなクリック感。 ちゃんと押さないとクリックにならない。


大好きでした。 もう使ってはないけど、今でも好きです。
俺はこいつらを使って 3DCG を始めたんです。 1994年に。


久しぶりに整理してたら出てきたけど、こりゃ捨てるわけにはいかねえ。
永久保存版ってやつです。
墓にでも入れてもらうか。




.

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

2011年5月27日 (金)

さらにチュートリアルビデオ大量 Softimage 7.x Certified Training。

来ました。待ってました。
http://www.si-community.com/community/viewtopic.php?f=22&t=1474&sid=e400dc6bb21bec4a15b520c623be042e

これまで、なぜか郵便受けさんが配布してきたチュートリアルビデオ大量。
Softimage 7.x Certified Training
YouTube で見れることになりましたよ!
先日の XSI4 のビデオに引き続き、
奥さん、これはすごいです

イエイ
長いこと待っていたんだ♪
そうこの時が来るのを♪



おそらく、嘔吐デスク様が、Adam Sale さんに依頼して作成したと思われるビデオ。
なぜ嘔吐デスク様が配布しないのだ? なぜ郵便受けさんに配布を任せたのだ?
その辺の経緯がよく分からない。


郵便受けさんは、非常に誘導的で、非常に分かりにくく、非常に不便な方法で配布を続けてきたんですよね。 メールを出すとメールが返って来て、色んなページの、必ずしも関係があるとは思えない色んな文章を読まされて、FileFactoryだかなんだかというファイル共有サイトに連れて行かれて、すんげえ細かく分割されたファイルをひとつひとつ落とさねばならず、しかも時間制限付き&帯域制限付き。 ダウンロード開始までも待たされたり、焦って落とそうとするとペナルティなのか、1日くらい落とせなくなったり。 すんげえ不便でしたよ。 でも俺は泣きながら、大半は落としましたよ。

そんな感じで不便極まりなかったにも関わらず、タダで配布してもらってるもんだから、ユーザは郵便受けさんに へへーーー とひれ伏してましたね。 配布方法に不満を持っていたのは皆同じで、 XSI Base などで誰かが違う配布方法を提案することもありましたが、郵便受けさんはすんげえ高圧的な態度で拒否を続けてました。

作者である Adam さんは、これはあくまでも想像だけど、嘔吐デスクとの契約上、自分で配布する権利を持っていないのではあるまいか。

郵便受けさんの態度に怒りを感じた Adam さんは、XSI Base 上で 「ちょっと郵便受けさん、著者として言いますけどね、あなた、なんか情報をコントロールしようとしてるように感じるけど? いい加減やめませんか? もっと普通の方法で、みんなが見れるように広く配布しすれば、XSI ユーザのすそも広がると思うけど?」 というような意味の発言(超意訳)をしてましたが、それに対して郵便受けさんからのレスは付いていなかったように見える。どうなったんだろう。


でもまあ、結果的に、YouTube で見れることになったので、過去のことは忘れましょう。 ひたすら恩恵を享受しましょう。 この Adam さん、何度も何度も書いてますが、ロジカルな説明と聞き取りやすい英語で、とても良いですよ。 お勧めであります。 リギングのチュートリアルと、ICE のが、とても充実しています。 毎日少しずつ見ましょう。




.

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

ビューポート上でmix8。

これ、長年の夢だったんじゃないかな。

ビューポート上でリアルタイムというかインタラクティブに、色やテクスチャの mix ができる RT コンパウンドです。 このビデオはだいぶ前に見てたけど、肝心のRTコンパウンドは公開されてなかったんですよね。 でもとうとう公開。
http://www.xsibase.com/forum/index.php?board=29;action=display;threadid=45058

スレッドの中にあるダウンロードリンクに行き、Télécharger ce fichier とかいう所をクリックするとダウンロードできます。 作者のクレメントさんはどうやらおフランスの人みたいなので、これフランス語のサイトでしょうかね。


ブツにマテリアルを与え、RTコンパウンドをドラッグ&ドロップなどで読み込み、RealTimeポートにつなげて、ビューを OpenGL にすると、効果が現れました。

クリックで大きな画像が出ます。
Oglmix8
この場合、1枚のポリゴングリッドにこの RTコンパウンドをかましました。
Base Layer に XSI 男のテクスチャを貼り、Layer1 にくるくる渦巻きなテクスチャを貼りました。 くるくる渦巻きはアルファチャネルを持っています。なので Layer1 の Input は Texture with Alpha というやつにしました。 んで、くるくる渦巻きには、ベースとなる XSI 男とは別の UV を与えています。 くるくる渦巻き用の Texture Support を SRT で調整し、望みの位置と大きさ、回転にしました。 

という作業が、インタラクティブに見ながらできる。
もしかして他のソフトウェアじゃ当たり前?
XSI の世界では、これで おお~ とみんながうなるんです。


テクスチャだけでなく、色そのものの調合にも使えます。つまりテクスチャの代わりに単色同士を混ぜてもいいし、テクスチャと単色を混ぜてもいいです。スヴァらしい。

混ぜる範囲と強度は、アルファチャネルだけじゃなくて、ウェイトマップでも指定できるようですね。まだ俺は試していませんが。

あと、画像の左上の所にあるように、XSI 男の Clip に Clip の PPG 上で色補正をかけているんですが(Adjustタブで Enable Effectsで有効にし、Brightness を上げたりしている)、結果はちゃんと反映されています。 がしかし、自動では反映されず、RT コンパウンドの Display Mode を Weights にしてからまた Textured Decal に戻すというような、表示に「刺激」を与えてやらないといけませんでした。 勝手にアップデートされると良いのですが、まあ、大した不具合でもありません。

ともかく、1つのレンダーツリーの中にある複数のテクスチャ(または色)を同時にビューポートに表示できるって、なんか嬉しいですね。 他のソフトウェアと違ってビューポート表示に力を入れてこなかった Softimage です。 個人のプラグイン開発者の好意に頼らないとこんなことすらできない XSIって 、なんてかわいいのでしょう。



mix8 の方は、なんだかバグがあるんですってよ。
表示が遅くなってしまうのかな?
だから特に問題なければ mix4 の方を使えとのことです。
俺の実験はmix4 だけです。
俺もそのうち mix8 試してみます。


あと、これは OpenGL 表示を使うだけあって、ビデオカードやらの性能や仕様に左右されるもんですかね? XP32bit + Quadro1500 ではそもそもテクスチャが表示されませんでした。単色も表示されなかったと思う。よって全く使えなかった。  一方、Win7 64bit + GeForce9800GT では問題なく動いています。

よく見てみると、Compatibilityフォルダになにやら古いビデオカード用のコンパウンドを入れておいたとかどうとか書いてありますね。そちらを使えば上記の XP32bit + Quadro1500でも動くのかもしれません。試してみます。  あと、俺が試した WinXP32 も Win7 64 も XSI のバージョンは 2011SAP SP1 です。 作者の書き込みによると、7 や 2010 に対応したバージョンもこれから出す予定みたいですね。

追記:

やはり、Compatibilityフォルダに入っている mix3 や mix7 という、どうやら古いバージョンらしい RT コンパウンドを使うと、XP32bit + Quadro1500 + XSI 2011SAP SP1で、ちゃんと動きました。

ブレンドのマスクにテクスチャも使うことができるんですね。 スヴァらしい。
あとは、Add とか Multiply のような、レイヤのブレンドモード的なこともできるとなお良いんですがね。





っていうかさあ、嘔吐デスクさん、XSI のビューポート表示、もうちっと進化させましょうよ。 今のアレのままだと、終わってますって。 3DS Max4.2 の頃だったかな、2002年とか 2003年とかそれくらいだと思うけど、チュートリアルでミカンか何かのテクスチャを貼っただけで、おおー Max は進んでるなあ XSI はダメだなあ  と思った記憶があります。 何がどうというのは忘れましたが、XSI にはできないことが平気で出来ていたという感じ。 それからもう8年だか9年だか経ちますが、XSI はあんまり変わってない気がします。





.

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

2011年5月26日 (木)

LoomのFluid。

わあ。

Sukio Sukio Sukio うっふん。 の Felix さんじゃないですか。
Loom の fluid を担当したんですね。

なになに、 CUDA ベースの流体シミュレータを書いたですと? そして、GPU を使ったボリュームレンダラも自前ですと?  すげえなあ、必要なツールは全部自前でやれちゃうんだ。 


その流体シミュレータ、directable と書いてあるから、何かしら制御しやすいつくりになってるんでしょうね。 いじってみてえなあ。 ボリュームレンダラも、まさに今、XSI 野郎にとっては欲しくて欲しくてたまらんものじゃないですか。 売り物にしてリリースしてくれないですかね。売れますよ多分。 Exocortex と比べてどうなんでしょう。っていうか Exocortex もいじったことありません。 ジェネラリストとして食っていくなら流体&ボリューム系は避けて通れないですかね。

ボリュームシェーダじゃなくてボリュームレンダラって書いてあるから、少なくともメンタル霊のシェーダではないでしょうね。 完全に独自なんですかね? ICE を使った、emRPCのようなシステムとか? よくわからん。

ちなみに Loom のレンダリングは全部メンタル霊だそうです。
そうか、メンタル霊でよくここまで塗ったな。



ところで Psyop さん、こんな優秀な男を手放してしまっていいんですか。




.

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

2011年5月25日 (水)

関節のお勉強。

おおっ すごく勉強になりそうな論文がっ
http://www.xsibase.com/forum/index.php?board=6;action=display;threadid=45052

juanさんというリガーなお方が、論文を作ったようです。



上のスレッドからリンクされている PDF
http://www.sod4602.com/misc/jc_kinesiology4riggers.pdf




関節に関する論文ですね。



juan さん曰く、

今までリガーとして体の動きについて研究してきたが、必ずしも運動学・生理学的な観点から研究してきたわけではなく、解剖学的な見地から観察していたに過ぎない(結果どう見えるかを観察していただけに過ぎない)

だから動きが不自然になりがちだった

なら生理学的に考えてみよう

筋肉の研究でも骨の研究でもない。その2つの組み合わせである関節の研究である

どの動きは、どこで起動され、どこへどう伝えられるのか

そういうことを研究して、より自然でリアルな関節の動きを描写できる図解を作ることを目標としよう


・・・・・ XSI Base 上での発言と、論文の冒頭のあたりをなんとなくまとめると、こういう主旨だと言っているように思えます。 英語原文の日本語訳としてはかなりアヤシイですが、大まかな主旨はこういうことなんだろうと俺は解釈しています。




ちょっとだけ読んでみましたが、例えば、 腕を上げようとしたら ○度から○度の角度までは○○関節が動き、それ以上は○○関節も起動され、最終的に○と○は一直線にならなくなり・・・・・うんぬんかんぬん というような、関節の挙動・性質を描写しているように見えます。

ただやみくもにロトスコープしたり写真を見たりして目標とする絵に近づけたりするのではなく、関節の仕組みを理解して、関節の挙動として何か可能で何が不可能か、どう並ぶか、どこが引っ張られるのか、などを知った上でやれば、より自然なポーズ、自然なデフォーメーション、自然なモーションができるという、そういう考え方なんだと思います。

関節だけの理解じゃダメなんでしょうが、関節という観点も大事、ってゆうか一番大事、というくらいのつもりで書かれた論文なんじゃないかな?




医学的なコトバがいっぱい出てくるので、英語を読むのが非常につらいです。 しかもおそらくは現段階での無断転載を防ぐために画像をスキャンしたような PDF になっているので、文字がコピペできません。つまり辞書ソフトウェアや Web検索などにそのまま食わせられません。修行のようなつらさがありますねw  でもすげえ参考になる、というか新しい意識を持つことができそうな気がするので、宿題リストに入れておきましょうね。 こういうのは文章じゃなくて、授業形式がいいですよね。 どっかで関節セミナーやってくれないかなw  金払ってでも参加したいですね。


文中の joint というのは、単関節のことだと思います。 2つの骨から成る関節。
complex というのが、複関節のことだと思います。3つ以上の骨から成る関節。
http://ja.wikipedia.org/wiki/人間の関節一覧





3DCGのアニメーションを見ると、なんとなく、鎖骨の動きが少ないものが多い気がするのは俺だけですか。 鎖骨の動きと言うと、結果的に肩の上下左右前後の動きになると思うんですが(左右は少ないか?)、 肩が動かないとポーズとしてすんげえ不自然ですよね。 試しに片方の肩を反対側の手で抑えて動けなくした状態で腕を動かしてみると、肩が動かずに腕を動かせる範囲ってすんげえ狭いことがわかりますよね。 っていうか自然に腕を動かしたら、ほぼ全ての動きで肩がセットになって動く気がします。 だから肩のポジションを動かすための基点になる鎖骨のローテーションがすごく重要だと思うんです。

例えば、拳銃を両手で構えたポーズなんかで不自然なものを多く見る気がするなあ。っていうか自分がやってもいつも不自然になりがちな気がする。 おそらく、肩の前後位置や上下位置が最適でないのだと思います。  最近話題になっていた、逆再生なゾンビものムービーでも、肩周りの動き・ポーズ・デフォーメーションがあまりにもひどくて、なぜみんなそこを気にせず絶賛するんだろうなあ、なんて思ってましたエラソーですいません。

あと、例えばケータイでメールを打っているポーズとかかな。そういう感じのポーズでも不自然なものを多く見る気がするんですがどうですかね。 ケータイを胸の前に持っていく時にどの程度肩が動くかとか、意識するとだいぶ良くなると思うし意識しないとひでえデキになると思います。

なのでセットアップ段階では鎖骨のピボットの位置など気を遣うわけですが、今まであまり上手く行ったと思えたことはありません orz  難しいですよね肩まわりのセットアップって。 まだ超本気でやったことはありません。   アニメーションの時点では鎖骨が動くタイミングと量がもちろん大事になると思ってます。 ヒジなどの関節と比較して、どの動きの時はどこが最初に動くか、とか。


なんか juan さんの関節研究な話からはそれてしまったような気もするがまあいいや。






さらに話はそれますが、なぜか俺、いつも人間のスケルタルな動きって不思議だなあと思ってしまうのです。 どこが起源なんだ? という感覚というか。 

関節を曲げますよね。例えばヒジを曲げるわけですが、3DCGのFK的に考えると、前腕(下腕)のボーンをローテーションさせるじゃないですか。 つまり骨が主体的に動く。 骨が起源です。 でも実際の人間の動きの仕組みでいうと、前腕の骨が自律的に動くわけじゃないですよね?  前腕に接続された腱が、前腕の骨を引っ張るんですよね? 違う?  

仮にそうだとすると、ヒジを曲げる動きってのは腱の動き、つまり腱が伸縮する動きが起源になっていることになりますよね。 

でも、じゃあ、腱の伸縮する動きの起源になっているものは何? 究極的には脳からの信号ということになるんでしょうが、そのリンクがなんか曖昧な感じがしてしまうのです。 腱が骨を引っ張ったりするのは物理的でわかりやすい。 でも腱が伸び縮みする動きを駆動している物体って、なんだ? と思ってしまうのです。

 脳 → 腱の伸び縮み → 骨が動く → 結果骨に接続された筋肉が硬くなったり膨れたり伸びたり

これ合ってる? 違うか? そもそも腱と筋肉の違いもわからんぞ俺は? すいません、大して勉強していないので、その辺の仕組みがよくわかってないのです。


で、そんなことを考えながら、ヒジを曲げたり伸ばしたり自分の体を動かしてじっと見ているうちに、脳から腱に伝えられる伸縮コマンドは WiFi で飛ばされているんじゃなかろうか、いや神経だかなんだかという有線LANのはずだよな、でもコマンドはどうやって受け取るんだ? 神経側にインタプリタのような機能があるということになるのか? 信号は本当に何かがそこを動いているのか? 例えばカンセツマゲルミンなどという名前の物質が脳から首や肩を通って腕まで一瞬で流れるのかな? それって液体? 電気信号? 電気ってビリビリってくるあの電気の微弱なやつってこと? とかなんとか思いが駆け巡って、しまいにはこの腕が自分の腕ではないような、ヒジを曲げるというごく自然なはずの行為がものすごく神秘的で科学では解明不可能なしくみだと思えてくるような、とても不思議な感覚に陥ります。 これってゲシュタルト崩壊っていうんだろうか。 子供の頃、寝る時に布団の中で、宇宙に浮かぶ地球がありその上で暮らしている自分たちの存在そのものや今そのことについて思考しているという事実が不思議で不思議でたまらなく、なんだか怖くて眠れなくなったあの感覚に似ています。




まあ、そんなことはどうでもいいですね。
大事なのは関節の挙動ですよ。
深く考えてるとゲシュタルト崩壊どころかスケジュール崩壊になるのでもうやめましょう。





.

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

2011年5月24日 (火)

Loom。

わあ、すげえ。

3Dは XSI だそうです。
ICE を死ぬほど使ったそうです。

1年かけて作ったそうです。
1年? そんなにかけてるのか。

https://groups.google.com/group/xsi_list/browse_frm/thread/a8e38cf50bbf8897?hl=en
http://www.polynoid.tv/loom/


レンダラはなんでしょね?
ML のスレッドが伸びてまた情報があがってくるかもしれません。



.

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

アニメCGセミナー。

先日アニメCGセミナー見てきたんですがね。
https://www.webcas.too.co.jp/form/fm/dms/anime07
Max なセミナーでした。

サンジゲンによるプリキュアのオープニング、ほんと半端ないですね。すげえクオリティだと思う。文句を付けたくても文句のつけようがなくて腹が立ちます。 俺も、こういうことやりたかったはずなのになあ。7年半前の決断はそのためだったんじゃなかったっけなあ。今やもう、それは無かったことにして欲しいくらいだよ。まあいいや、ええと、要するに、サンジゲンの作品を見るといつも凹みます。

会場のスクリーンでは色がすげえ変でした。あれはスクリーンのせいだよね? ちゃんと見てみたいな。


元々はセル調の質感ではなく、グラデーションのあるCGっぽい質感だったそうですが、その質感ならサンジゲンでやる意味はないでしょう、他の会社に頼んだ方がいいでしょう、うちでやるならセル調にしましょう、と松浦社長が提案して、結局セル質感になったという話がありました。 松浦社長は、セル調でかつリミテッドアニメーションでなければサンジゲンの意味は無い、という意味のことを明言してました。 ほうほう。そこまで言い切るのはすげえ。完全にその路線のみで行くつもりなんですね。そんな風に仕事の選択肢を狭くしない方がいいのではないかと個人的には思うんだけど、でもここまでビジョンがはっきりしているならそれは大いに強みになる。 明確なビジョン。ブレがない。スヴァらしい。どっかの社長とは大違いです。





このオープニング、歴代のプリキュアが21人も出てるそうですね。東アニが元から持っていた Maya データを、サンジゲンが使う Max へサンジゲン側でコンバートしたようですが、1~2ヶ月もかかったそうです。ただし詰めた2ヶ月ではないそうです。

そんなコンバートの手間があることは承知で、東アニはそれでもサンジゲンに出したかったんでしょうかね。そりゃそうでしょうね。あのクオリティだもの。その価値はありますよ。

形状のコンバートはまあ問題ではないでしょう。 ただ、意外だったのは、モーフターゲットも東アニの Mayaデータからコンバートして使ったとのこと。ほうほう。今どきそういうことも安全にできるんですか。なんか俺、他のアプリケーションとのやりとりの経験が少なくて、この辺のこと知らなさ過ぎです。昔は頂点の順番が変わったりなんだりで、モーフターゲットまでコンバートなんてけっこう危険だったですからね。大昔の話ですが。

東アニのモデルデータは基本的にオブジェクトが分かれまくっているそうです(まつ毛1本までバラのオブジェクトだそうで)。 サンジゲンとしてはある程度マージ(アタッチ)してしまいたい。でもただマージすればいいのではなく、表情用のモーフターゲット全てで同じ順番でマージしていかないといけない(頂点番号が狂うという意味だと思う)。 でも順番狂わないように気をつけてマージしていくなんて、とてもじゃないけど手作業じゃやってられないほどの物量である。ということで自動マージツールを作ったと言っていました。まあ当然ですな。そういうのはツール作らないとダメです。

東アニのデータは、オブジェクト名などがとても美しく整理されていたため、ツールの開発も楽だったそうです。スヴァらしいですね。どっかの会社とは大違いだ。 ツールを作る側の立場で言わせてもらうとですね、データが未整理だとツールって作れないんですよ。当たり前ですがね。


マテリアルのコンバートは、ランプシェーダなど Maya特有のシェーダはあらかじめMaya側で外しておかねばならなかった、という意味のことを言っていましたが、ってことは、大半のシェーダはそのままコンバートできるって意味なんですかね? 複雑なレンダーツリー(ハイパーシェードって言うの?)を持ったようなものでもいいんですかね? 俺はマテリアルなんて大半はコンバートできないんじゃないかくらいのイメージを持っていたので、よくわかりませんでした。今どきすげえコンバートできるのかなあ。 FBX を使ったと言っていました。

東アニモデルの「目」は、全てモデリングされていたそうですが、サンジゲンはテクスチャでやるのが普通だそうで、目は全部作り直してテクスチャを貼ったという意味のことを言っていたと思います。さすがサンジゲン、我が道を行くというか、俺様のやり方でしかやらないと言うか。



リグはさすがにコンバートできないので、参考にした程度だそうです。まあ、それこそサンジゲンは俺様リグでやらねばならんでしょう。人様リグでサンジゲンの動きを作るのは難しいんじゃないかと想像しています。

骨は Bones Pro を使ったそうですが、Bones Pro って何? 今調べてみたら、スキニングのプラグインのようですね? http://www.bonespro.com/  XSI でいう所のエンベロープの設定、ウェイトの設定、筋肉デフォーメーションの仕込み、その辺が統合的にいじれるパッケージのように見えます。ほうほう、なんかいかにも Max らしいプラグインですね。もし XSI なら ICE のコンパウンドの形で出てくるんでしょうなあ。

Bones Pro は「軽くて簡単」、と言ってましたが、何がどう簡単だったという詳しい話はありませんでした。

「ボーンオブジェクトは Biped です」 と言っていたけど、Max のリギングのことをよく知らない俺には、意味はわかりません。 

あと、「Bones Pro はライセンスのないマシンでもプロキシで使えるから良い」などと言っていたと思うんですが、これも意味はわかりませんでした。 たぶん、ライセンスがあると Bones Pro を使ったデータの編集ができる、ライセンスがないと編集はできないけどちゃんとシーンを開けるし、Bones Pro の効果は外れてない状態だ、という意味だと思います。 Max のプラグインにそういうアーキテクチャがあるのかな。 AfterEffects のプラグインなんかもそうだとすごく助かるんですがねえ。ま、レンダリング用の安いライセンスを用意している製品もあると思うのでそれでもいいんですが。


揺れ物にはスプリングモディファイヤでリグを組んだと言ってましたね。でもパラメータをいじるのが大変なので、そのためのツールを作ったと言ってました。期間限定で配布もすると言ってたな。 おそらくは多くのパラメータをイッキに書き換えたり、結果を SRT にプロットをしたりするツールなんだと思います。 配布するくらいだからある程度以上汎用性があるんでしょうね。 ただ、この手のツールはヘタに汎用性を持たせようとすると使いにくくなったり、開発がスピーディーにできなかったりしがちですよね。だから汎用性を捨てて、その仕事のためだけに書いたツールが一番作業効率がいいんですよね。当たり前ですが。






以上のような開発を経て、実際のカット制作では、

コンテ撮 → アニマティクス → 揺れ物・表情が無いテイク0 → テイク1~3(めり込み修正無し) → 完成

という流れだったそうです。 揺れ物と表情、そしてめり込み修正がいったいどの段階で入るのかという詳しい説明はなかったように思う。

オープニングのカット1ですか、冒頭部分を見せてもらいましたが、いやあ、すげえアニメーションだ。 俺にはとてもできない。このタイミングも中無し具合もツメも、レイアウトも、俺の中からは出てきません。惨敗です。まあ当たり前ですね。こればっかりを超本気でやっているサンジゲンに、特に本気と言えるほど詰めてやってこなかった俺が太刀打ちできるはずもありません。比較すること自体が傲慢に思えます。すいません。

カット制作のポイントとしては、かなり多くの項目を挙げていましたが、「ルックとしては、カゲの付き方が最も重要。 1コマ1コマペイント修正なんて当たり前」 「ハイライトも気に入った形になるようペイントで調整する」 「口の中の、舌が占有する面積の割合が重要」 「目の中の、白目と黒目の面積の割合が重要」 とか、その辺がほぉーーーと思いました。

アニメっぽく見せるためには何でもする、という気合を感じますね。ほぼイコール、CGっぽさを極限まで排除するということだと言っているように聞こえました。 そのためには1コマごとのペイントなんてザラのようです。 その手間、物理的にかける苦労はもちろん賞賛に値するものですが、やはり何よりすごいのは、この方針のブレのなさでしょう。絶対に方針がブレない感じがヒシヒシと伝わってきます。その意味においては、スタッフはむしろ気持ちはラクなんではないかと想像してます。ブレブレの中でやる作業はつらいものですからね。





あと、「プリキュアは子供向け作品なので、中無しの動きにコマを足して、子供でも見やすい動きになるよう調整した」 と言ってました。 これ、最重要ではないかと俺は思いました。 あのオープニングの動きが素晴らしく見える最大の理由なんではないか、とすら俺は思ってます。 


というのはほら、日本のアニメ作品の中で登場するCGモーションって、なんか誤解されたものが多かったじゃないですか。 「リミテッドアニメ風に見せたいから、ガンガン中無しにして、キレのある動きにする」 とかなんとか言いながら、実はただ速いだけでカッコ良くもなんともない、とても見づらい、目がチカチカする、何が起こっているのかさっぱりわからないモーションを、さんざん見てきました。 宇宙でロボットが自在に飛びながら、カメラもガンガン動いて追っかけながらロボットがビーム撃ったりライトセイバーを振り回したりする、そんな感じのやつでこういう「速いだけで何が起こっているかわからん」なモーション多かったと感じています。作画によるロボットの描写が伝統的にカッコいい大会社ほど、CGになった時にその傾向が強いと感じるのは俺だけですかね。

確かに作画によるモーションは、クイックで中無しなテイストのものが多いですが、アレは物体を自在にデフォーメーションさせながら描いているからこそ成り立っているわけであって、ただタイミングを真似ればCGでも同じようなキレのある動きになるわけではありません。作画ではモーションブラーすら手で描いてるわけですしね。 速いとか遅いとかタイミング的なこと(=タイムシート上のこと)だけじゃなくて、デフォーメーション、ブラー、オバケ、イン直後のポーズ、アウト直前のポーズ、などなど絵そのものの描き方(=作画用紙上のこと)を総動員して、見た人の目に焼きつく「残像感」とでも言うべきものを操作することによってある見え方に見せているわけで、これをそんなに簡単にCGで真似できるとは思えないです。 作画でラフ原描いてもらって、タイムシートまで入れてもらってるのに、CGでロトスコープしてそれをなぞってもさっぱりカッコ良くならないのは、これが原因だと思っています。

いっときは、作画でほぼ全部作ってしまってからそれをロトスコープで忠実に追いかけることによってCGで再現し、それを「作画っぽく見せるためにひとコマひとコマ追いかけました。そんな泥臭い手作業をしてまでクオリティにこだわっています」などと誇らしげに説明された時期もありましたが、いやいやそれは違うでしょう、デフォーメーションまで真似ているわけじゃないから全然カッコ良くないですよ、速いだけですよ、むしろ何も考えずに作画を追いかけてポーズ付けてるだけなんだから、そんなのシロートでもできますよ。 とは言いませんでしたが、心の中では思っていましたすいません。デフォーメーションまで忠実に真似れば、かなり良くなる(作画に近くなる)んじゃないのかなあ。 

俺はそこまで真面目にやったことはないですが、デフォーメーションさせないことを考慮して、その分少しコマを多めに取る(速くし過ぎない)傾向で調整してきたことが多かった気がします。 よくあるパターンで言うと、例えばカメラの前を通り過ぎる飛行機とかミサイルとか車とかを PAN で追っかけるようなカットの場合でしょうかね。普通にやったらカメラの前を通り過ぎるのは一瞬なので目に痛いだけの絵になりやすいです。作画だとその瞬間の前後に物体が進行方向に伸びたりしてますよね。しかも伸びてるだけじゃなくてぐにゃっと弧を描いた変形をしていたりね。そういうデフォーメーションがあるからこそカッコいい動きに見え、目にも痛くない感じになっている場合が多いと思うんです。でもCGの時はジオメトリを実際に変形させることまではめったにしない(主に手間の問題)。その代わりに、2コマか3コマかその程度、カメラの目の前のどアップの尺を長めにとってあげることによって、似た残像感を狙う。これだけでもだいぶ違うと思っています。この話をわかってくれるのはK村やF松やK島やH田や、あとはM山くらいか。 

ともかくですね、このプリキュアのオープニングでは、すんげえクイックな動きも含んだ、いわゆる作画的なタイミングのモーションに見えつつも、速いだけとか目に痛いとかは感じなかったんですよ。 これは、松浦社長が言っていた「中無しにコマ足して見やすくした」効果なんじゃないかと思うんです。社長、ちゃんと効いてますよこれ。子供向け作品じゃなくても、基本はこれでやる方が良いと俺は思います。 わかってやってるから、ちゃんとキレが出てますよ。ダルくなってないですよ。



余談ですが、タイミングとデフォーメーションの話を出してしまったのでついでに書きますけど、個人的にはポーズとデフォーメーションの関係も重要だと思ってます。 

伝統的に、3DCGは作画よりポーズがカッコ悪くなりがちだと思います。 これはやはり、上のタイミングの話と同じで、デフォーメーションさせないことが原因なのかな、と思うんです。作画だとまずポーズありきで絵を描くので、人間や動物ならポーズの都合に合わせて間接の位置は自在に変わるし、メカものなんかでは、体に付属している本来変形しないリジッドなパーツも、そのポーズに都合のいいように変形させて描いてしまうわけですよね。このようにあくまでもポーズ優先ですから、ポーズがカッコ悪くなりようがありません。 ポーズのために都合よく、あらゆるものをデフォーメーションさせているということです。

でもCGの場合はリグの都合がありますよね。間接の位置を自在に変えるなど、難しい。 また、めり込みの問題もあります。肩にでっかいアーマーが付いているために首を横に振れないロボットとかね。 結果、そもそもの絵の作り方がポーズを基点としていなく、リグなりモデルなりを基点としてしまっているので、カッコ悪くなりがちだと思うのです。ポーズに魂が入っていない絵になりやすい。

例えるなら、昔の戦隊もので出てくる巨大ロボットとかですかね? 役者が、美術で作られたロボットの外装を着込んで動かしているわけですから、実際に肩やヒジなど間接周辺のパーツに動きを制限されます。物理的にぶつかり合ってそれ以上曲げられないわけですからね。結果、硬い動き、硬いポーズしか取れないじゃないですか。 CGでめり込みを気にしてポーズに遠慮があると、この戦隊もののロボットに近い状態になってるなと感じることがよくあります。 あの硬さ。 あのポーズの不自然さ。 あの魂の入ってなさ。

間接の位置を変えるのはともかく、めり込みはポーズのためなら犠牲にしてもいいと俺は思いますね。めり込みを気にしすぎて自由にポーズを取れないという仕事をいっぱい見てきました。俺に言わせれば本末転倒です。「めり込むから首がここまでしか回せないなんておかしいじゃない? ここは体勢を崩しつつも目線だけは敵をグッとにらんだままのポーズを続けるからこそ、このロボットの意思を感じる絵になるわけですよ。 しかもこのロボットは眼球がないんだから、視線を感じさせるポーズにする方法は、首をそっちに向けるしかないわけですよ。なのにめり込む一歩手前までしか首が回ってないから、ほらあさっての方を向いているみたいだよ。これじゃ敵と本気で戦っている感じがしないよ」 などと言いたいのです。 野球のピッチャーの投球のポーズを見ても、背中とかすげえ反っているじゃないですか。腰や背中のパーツがめり込むからって体の反りっぷりに遠慮があったら、そりゃカッコいいモーションに見えなくなっちまいますよ。

どーしてもめり込みが気になるなら、もうその周辺の頂点を動かすリグを組んで、作画によるデフォーメーションのごとく、めり込むパーツを自在に変形させて回避するしかないでしょうね。でもそれはすごい手間ですね。スケジュール的に破綻しそう。手間・時間を理由にそれを断念するのであれば、どうかめり込み回避をあきらめて欲しいと思うのです。めり込みを回避するためにポーズをあきらめるのは、いかん。いかんのです。逆です。ポーズのためなら、少しぐらいめり込んでいたっていいことにしましょうよ。

リアルタイムものだと、デフォーマの数に制限があったり、リグをカットごとに変えられなかったりするので、やはりめり込み回避リグを組むのはあまり現実的ではないかもしれませんね。 そうなるとやはり、めり込み回避をあきらめて欲しいと思うのです。めり込みを回避するためにポーズをあきらめるのは、いかん。いかんのです。逆です。ポーズのためなら、少しぐらいめり込んでいたっていいことにしましょうよ。

でもリアルタイムものが多いと思われるゲーム会社さんなどは、めり込みをすげえ気にしますよねえ。俺には不思議でなりません。めり込んだっていいじゃないか。CGだもの。みつを。 いや、CGだからめり込んでもいいとかは別に思っていませんが、何をあきらめて何を取るかの判断がおかしくないかな? と思うことが多いです。カッコ良くしたいんでしょ? めり込みは無いけど遠慮がちでこじんまりしたポーズの絵と、めり込みはあるけどガッチリ魂の入った生きているポーズの絵と、どっちが見たいですかと聞いてみたい。俺がそんな話をできる立場になったら、聞いてみよう。



おおっとサンジゲンから大いに話がそれてしまった。 でもあながち無関係の話ではない。 というのは、松浦社長曰く、顔の見え方などが意図どおりになるように、ゆがませるリグなどを仕込んでいるという話でした。 ほら、やはりデフォーメーションは必要なんですよ。サンジゲンもやってるんですよ。そういえば、キャラが大きなハンマーを振るときに、ハンマーをぐにゃっと変形させているというサンジゲンの記事を読んだことがあるな。スケールアニメーションをしたりとかも。そう、それです。そういうのがあるからこそ、「速いだけ」ではない動きに見えるはず。

松浦社長曰く、「カットごとにいじることが前提のモデルを作る」 だそうで、どうせカットごとに修正するんだから、それがしやすいようになっているモデルが良いとのことでした。 完璧なモデルをあらかじめ作り上げるのはあまり意味がない。どーせいじるんだから。だからモデリングとかには時間をかけずに、なるべく早くアニメーションに入るようにするそうです。

個人的には大賛成であり、俺もなるべく今までそうしようと努力してきた気がします。 何が必要で何が不必要かって、本番カットのカメラから見てみないとわからないですからね。 なので最低限の開発を終えたら、さっさとアニマティクスを作るのが一番良いと思ってます。アニマティクスでカメラやら尺やら決め込んでしまい、あとはそこで見える部分・重要な部分だけを作ればいい。 リグなんかも、気合入れてエクスプレッション書いた複雑な仕組みが、本番カットでは1回も使わなかったなんてこともよくあります。 なのでシンプルリグでアニマティクス作ってしまい、その動きを実現するためにはどういうリグが必要なのかを先に知ってから本番リグの開発を始めても遅くない。 キャラだけじゃなくて背景とかでも同じことですよね。要らない背景を作る必要はない。アニマティクスを作らないとそれが見えてこない。

ともかく、サンジゲンではほぼ確実に、どのカットもカットごとにいじるわけですね。 サンジゲンにとって「いじることが前提の、いじりやすいモデル」とはどういうモデルなのか、知りたいです。 松浦社長はきっと「それを知るにはサンジゲンに入ってください」と言うのでしょうが。

いじり方の例として、半袖の衣装の袖口に落ちるカゲの例とか出してましたね。 複雑な形の袖口なものだから、腕に複雑な形のシャドウが落ちているわけですが、これは絵がごちゃごちゃになってしまうだけなのでアニメ的にはあまりよろしくない。シンプルなシルエットのカゲにしたい。だからペイントして直す。 という感じでした。 あとは横顔用のモーフターゲットなどをその都度作ったり、鼻の位置を変えたり、FFD って言うの?ラティス変形のようなものでデフォーメーションさせたり・・・・ あまりにもカットごとにいじることが当たり前になり過ぎてしまったために、もしたまたま3Dが一発で決まって手作業する必要がないカットが出てきたりしたら、まるで手抜きをしちゃっているかのような気分になったりするのでしょうね。


ところで、一連の話は全てプリレンダを前提にしている考え方のように聞こえますね。リアルタイムだとカットごとにいじれることが大幅に制限されます。サンジゲンはリアルタイム仕事はしないのかなあ。しないつもりなんだろうなあ。「セル調でリミテッドじゃないと」って明言してるくらいだかならなあ。まあ、「セル調でリミテッド」自体はリアルタイムでも可能だけど、サンジゲンの場合は「カットごとにペイントとかでいじったセル調」だもんなあ。 リミテッドってのも、後からコマ抜いたりとかもしてるんでしょ? いわゆるシート操作。 それはリアルタイムでは難しいよなあ。


21人がいっぺんに出てくるダンスシーンでは、モーションキャプチャを使ったそうです。 なんだモーキャプかあ。サンジゲンなら手付けで通して欲しいんだけどなあ。 ま、手間というか時間のの問題でしょうなあ。  ただし、キャプチャデータをそのまま使ったわけではなく、アニメーションレイヤ機能を使っていじくったのはもちろんのこと、プロットしたデータを直接いじってコマ抜いたり、やはりサンジゲン流の調整は入れているとのことです。 あとはカメラのみフルコマで、キャラの動きは2コマにしてると言っていたかな。 会場のスクリーンだとその辺よくわからなかったなあ。DVD買うか。7月発売か。本編中の絵も全般に良いデキだろうから、買っても損はないか。 アマゾンで買うことにしよう。 レジでキラキラピンクのパッケージを店員に渡して、聞かれてもいないのに 「いや、娘にね・・・」 などとつぶやく気恥ずかしさも味わわずに済む。

しかしなんだこれは。色盲のテストかこのサイトは。
http://www.precure-allstars.com/



サンジゲンの話おわり。いやあ濃いセミナーでした。面白かった。

サンジゲンは、その仕事を完遂することそのものよりも、ノウハウを確立するとか蓄積するとか、そういうことに重点を置いているような感じがするんですが、どうですかね。 毎回終わらせることそのものに終始している俺には、まぶしい会社です。

飛ぶ鳥を落とす勢いのサンジゲンさんは、いつもスタッフを募集してますね。
http://www.sanzigen.co.jp/recruit.html
作画経験者優遇だそうで。さすがですね。
あれ? 土曜日はお休みじゃないのか? 週休1日制?








その後登場したサンライズさんは・・・・・・うん、まあ、その、ゴニョゴニョ  ・・・・プレゼン能力の重要性というものについて考えさせられました。 なんつうか、まるで自分がガンダムユニコーンの仕事を請け負っていて、その初回の打ち合わせに出席しているかのような錯覚を覚えました。以上。

ああ、でも内容は、俺にはすんごくよくわかりましたよ。CGガイド。 コックピットや会議室など。 俺もさんざん、さんざんそういう仕事をやってきましたし、実は今もやっています。 へへへへ、俺にほんとに発注してくれたら、こういうのは速いぜよ。

にしてもサンライズさん、作画へのアタリ出しに関するノウハウなど、すげえ持っていそうだよなあ。 作画や美術のことをちゃんとわかってやっているCG屋って少ないですからね。強みになりますよね。逆にCGのことをわかってやってくれる作画や美術や演出もまだまだ少ないですけどねえ。

ところで Max のビューポートってどうしてあんなにカラフルなんでしょう。 あの、ホワイトベースみたいな、アーガマだっけ、それのモデルデータとか見せてもらいましたが、 ワイヤカラーがすごいことになってるんですよね。 Max の作業画面を見ると、いつもこうですよね。 デフォルトだと、オブジェクト1個ごとに違うワイヤの色が割り当てられるんでしたっけ? Max なみなさんは、アレで平気なんですか? 目がチカチカしないんですか? 不思議です。面白いからそういうプラグイン書いて XSI でも似たような状態で仕事してみようかな。

あと、マテリアルのセットというものがどういうものなのか、よく分からなかったんだけど、気になりました。色指定ごと(色替えごと)にマテリアルのセットを持っているようなことを言っていましたね? それって、XSI で言うところの Pass で違うマテリアルにできるとか、それに近いものなのかな。








その後は嘔吐デスク様による、Motion Builder, Max, XSI の連携のデモのようなもの。 なかなか面白かったです。まあ内容は最近よくある「ボタンひとつでデータ持って行きます~ はい ICE で煙出して Max に戻します~」的なものだったので全然大したことなかったですが、なかなか XSI 以外のソフトウェアを見る機会も少ないし、このイベントのように Max を中心として、あくまでも Maxユーザの視点からしている話というのは、参考になるものです。

Motion Builder やってみたいですねえ。一度も触ったことありません。覚えさせてくれる仕事ないかな。持ってないけど。


あ、あと、Lagoa の紹介をする時にこのムービー使ってました。
ポーランドの piotrek marczak さんによる作品なわけですが、そういう断りはなかったなあ。許可はもちろん取ってあるんだろうけど・・・。

こういう粘性の高いやつもできますよー こういう硬い感じもできますよー みたいに喋っていて、まるで嘔吐デスク様が作ったかのような印象を与える感じのプレゼンだったので、微妙でした。 また、 piotrek さんがガチで Lagoa だけでこの状態にしたって、確認取ってるんでしょうかね? ま、Lagoa Tests というタイトルのムービーなので Lagoa がメインのはずですが、意外と Lagoa 以外での調整も入ってるかも知れないなあと思ってね。 例えばキャッシュ化した後に、それをさらにいじる非Lagoa な ICE ノードで調整してたりとかしないのかなあ、って。仮にそうだとすると、Lagoa の能力の宣伝の仕方としてはちと問題があるかもしれず、しかも Lagoa や ICE のことをそれほど知らないであろう Max ユーザに対する話なのでますます微妙な宣伝になってしまいます。なんだか心配になりましたです。 また、レンダリングを V-Ray と言っていましたが、Arnold の間違いです。



もうひとつ、全くどうでもいい話ですが、ICE の発音(アクセント)ってどれが正しいのでしょうか。 今回プレゼンした嘔吐デスクのお方は、アイスのことを、「座椅子」と同じアクセント(平坦なアクセント)で発音していました。 俺はこれまで、いわゆるアイスクリームの意味で発音するときのアイス、つまり「ライス」「ナイス!」と同じアクセント(1文字目にアクセント)で発音していました。 どちらが正しいのか、気になって夜も眠れません。

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

2011年5月20日 (金)

大昔のチュートリアルビデオ大量。

これはスヴァらしい。

http://www.si-community.com/community/viewtopic.php?f=22&t=1451&p=10342



Manny さんがアップロードしてくれたのかな。
大昔の、XSI4 の頃のチュートリアルビデオです。
わあ、YouYube で見れるんだ。 スヴァらしい。



たしか 4枚組みくらいの DVDで、500ドルとかだった気がする。DVD-ROM だったかなあ。専用のプレイヤで見るんだった気がする。DVD-Video ではなかったと思う。 どこへしまったっけ。

日本の代理店を通して買うと、日本語化されているわけでもないのに7万円くらいになっていた気がする。 Foundation があった時代で、ソフトウェアの値段とほぼ同じかそれより高いチュートリアルビデオでしたね。


このビデオはずいぶん見ました。基礎的なことがすんげえわかりやすく解説されていました。XSI は 4 からかなり改良された印象だったので、新しい機能やワークフローを知るのにも助かりました。 アンチエイリアスの仕組みとか、そういう根っこの概念を理解するのにも役に立ったな。 もう6年か7年も前の話だな。


しばらくして Vast とかいうサイトに全てのビデオがアップロードされて無料で開放されたんだけど、アメリカ以外の国ではちゃんと見れなかったんですよね。 なにか国による制限があったようです。 日本から見れたこともあったけど、ほとんどの場合見れなかったと思う。 それがいまや YouTube で見れるんだから、ほんとにスヴァらしいですよ。



古いビデオだからと言って侮るなかれ。
今でも当時と変わってないためにそのまま適用できる話がいっぱいあるはず。
今さら誰にも聞けない系の話もいっぱいあるはず。

そしてこのビデオを作っていたのは、当時の Softimage の人たちだったわけですよ。
開発者自らが解説しているわけです。
Digital Tutors のような、ソフトウェアのことも映像作りのことも全く理解していない人が作る、ほら簡単でしょ系の美辞麗句的チュートリアルとは違います。

全ての XSI 使いにお勧めします。
ほんとですってば。
俺、当時毎日寝る前にこれ見てたんだから。
ベンキョになりました。

深夜にヘッドフォンして英語を聴いていると、いい感じで眠くなるんだよね。
3つくらい見て眠りこけてしまう。
翌日に、前日の最後の1つはほぼ覚えてないから、そこからスタート。
3歩進んで1歩下がる。
水前寺清子的に行きましょう。






.

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

2011年5月19日 (木)

超 Isolateレイヤ。

また小物スクリプトを書き逃げしますよ。



Sel = Selection.GetAsText(); //選択中のブツを保存
var oLayers = XSIFactory.CreateObject( "XSI.Collection" );
//Layer入れる箱用意
oLayers.Unique = true;
// 選択中のブツを全部ループしてレイヤをたどるが、同じレイヤに属している複数のブツから取得した場合にダブってしまうのを回避
for ( var i=0; i<Selection.count; i++ )
{
    if ( Selection(i).IsClassOf( siX3DObjectID ) )
    {      
        var oOwners = Selection(i).Owners;
        for ( var j=0; j<oOwners.count; j++ )
        {
          
 //オーナーの中からレイヤを探して取得
            if ( oOwners(j).IsClassOf( siLayerID ) )
            {
                oLayers.Add( oOwners(j) );
                break;
            }
        }
    }
}

Logmessage( oLayers );
Selection.SetAsText( oLayers );
// レイヤを選択状態にする(Isolateは選択に依存するため)
IsolateSelected( null, 0);
//全部のビューで Isolate する
IsolateSelected( null, 1);
IsolateSelected( null, 2);
IsolateSelected( null, 3);
Selection.SetAsText( Sel );
//もともと選択していたものを選択し直しておしまい。





今の作業的に結構便利なんですこれが。

それなりに複雑なシーンで、不本意ながらレイヤを使った整理に大いに頼っているんですがね。 シーンの中の、前側とか右側とか上側とか、そういう空間的なエリアごとにレイヤに分けている。 あるエリアのみ表示したい。そのエリアのみ表示して作業したいということが多いのです。

なので該当するレイヤを選んで Isolate すればいいだけなんですが、レイヤをいちいち選ぶのはめんどくさいわけですよ。

なので、レイヤそのものを選択するのではなく、現在選択しているオブジェクトが所属するレイヤを勝手に探してそれを Isolate するというスクリプトにしてみたら、アラなかなか便利ですわよ奥さん。 Explorer もしくは Scene Layer Manager からレイヤを選ぶ必要がない。オブジェクト(複数可)を選ぶだけでよいわけです。




超 Isolate パーティションもそのうち書こうかな。
ごきげんよう。



.

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

2011年5月18日 (水)

パッていしょん。

またもパーティションがらみのスクリプト(のベータ版)です。




if ( Selection.count !=0 && Selection(0).type == "Partition" )
{
    DefPartitionName = Selection(0).name;
}
else
{
    DefPartitionName = "";
}


//    ############################################################
var oP = XSIFactory.CreateObject( "CustomProperty" );
oP.name = "パッていしょん";

oP.AddParameter2( "sPassNames", siString, "" );
oP.AddParameter2( "sPass", siString, "" );

oP.AddParameter2( "sPartitionName", siString, DefPartitionName );
oP.AddParameter2( "bSkip", siBool, true );



//    ############################################################


var oAllPasses = ActiveProject.ActiveScene.Passes;
PassNames = "";
var aListItems = new Array( );
for ( var i=0; i<oAllPasses.count; i++ )
{
    aListItems.push( oAllPasses(i).name );    //    for ListBox
    aListItems.push( oAllPasses(i).name );    //    for ListBox
   
    PassNames += oAllPasses(i).name;    //    for internal reserve
    if ( i != oAllPasses.count -1 )
    {
        PassNames += ";";    //    separator
    }
}


//    Store the value in a parameter for the sake of being able to access from PPG logic

oP.sPassNames.value = PassNames;



//    ############################################################
var oL, oItem;
var oL = oP.PPGLayout;

        oL.AddGroup( "Target Passes", true, 50 );
            oL.AddRow( );
                oItem = oL.AddButton( "SelectAllPass", "All ON" );
                oItem.SetAttribute( siUICX, 150 );
                oItem.SetAttribute( siUICY, 16 );
                oItem = oL.AddButton( "DeselectAllPass", "All OFF" );
                oItem.SetAttribute( siUICX, 150 );
                oItem.SetAttribute( siUICY, 16 );
            oL.EndRow( );
       
       
            var oListBox = oL.AddItem( "sPass", "", siControlListBox );
            oListBox.SetAttribute( siUICY, oAllPasses.count * 20 );
            oListBox.UIItems = aListItems;
            oListBox.SetAttribute( siUIMultiSelectionListBox, true );
            oListBox.SetAttribute( siUINoLabel, true );
        oL.EndGroup( )
       
        oL.AddGroup( "Target Partitions", true, 50 );
            oL.AddRow( )
                oItem = oL.AddItem( "sPartitionName", "Name" );
                oItem.SetAttribute( siUINoLabel, true );
                oItem = oL.AddButton( "Pick" );
            oL.EndRow( )
            oItem = oL.AddItem( "bSkip", "Do not create partition if already exists" );

            oL.AddRow( )
                oItem = oL.AddButton( "Create", "Create Partition" );
                oItem.SetAttribute( siUICX, 95);
                oItem.SetAttribute( siUICY, 30 );
                oItem = oL.AddButton( "Move", "Create && Move Selection to Partition" );
                oItem.SetAttribute( siUICX, 205 );
                oItem.SetAttribute( siUICY, 30 );
            oL.EndRow( )

        oL.EndGroup( )

oL.Language = "JScript";
oL.Logic =    OnInit.toString( ) +
            allon_OnClicked.toString() +
            alloff_OnClicked.toString() +
            Pick_OnClicked.toString() +
            Create_OnClicked.toString()+
            Move_OnClicked.toString()+
            DoTheGodDamnThing.toString()+
            PickPartition.toString();

function OnInit( )
{
    allon_OnClicked( )
}
function allon_OnClicked( )
{
    PPG.sPass.value = PPG.sPassNames.value;
}
function alloff_OnClicked( )
{
    PPG.sPass.value = "";
}

function Pick_OnClicked( )
{
    var oPartition = PickPartition( );
    if ( oPartition != null )
    {
        PPG.sPartitionName.value = oPartition.name
    }
    else
    {
        Logmessage( "ドルァ" );
    }
}

function Move_OnClicked( )
{
    DoTheGodDamnThing( "move" );
}
function Create_OnClicked( )
{
    DoTheGodDamnThing( "create" );
}



function DoTheGodDamnThing( mode )
{
    var oTargetPasses = XSIFactory.CreateObject( "XSI.Collection" );
    var aPassNames = GetSelectedPassInPPG( );
    for ( var i=0; i<aPassNames.length; i++ )
    {
        var oPass = Dictionary.GetObject( "Passes." + aPassNames[i], false );
        if ( oPass != null && oPass.type == "Pass" )
        {
            oTargetPasses.Add( oPass );
        }
    }
       
    if ( oTargetPasses.count == 0 )
    {   
        Logmessage( "Pass がひとつも選ばれてねえよ", siError );
    }            
    else
    {
        PartitionName = PPG.sPartitionName.value;
        Flag = 0;
        if ( PartitionName == "" )
        {
            var oPartition = PickPartition( );
            if ( oPartition != null )
            {
                PartitionName = oPartition.name;
            }
            else
            {
                Flag = 1;
            }
        }

        if ( Flag == 0 )
        {
            for ( var i=0; i<oTargetPasses.count; i++ )
            {
                var oPass = oTargetPasses(i);
                var oPartition = oPass.Partitions( PartitionName );
               
                if ( oPartition == null )
                {
                    var oPartition = oPass.CreatePartition( PartitionName, siObjectPartition );
                    Logmessage( oPass.name + " に " + oPartition.name + " 作りマスター!!!!");
                }
                else
                {
                    Logmessage( "******** " + oPartition + " は既に存在してまスター!!!!。" );
                    if ( ! PPG.bSkip.value )
                    {
                        PartitionName += "1";
                        var oPartition = oPass.CreatePartition( PartitionName, siObjectPartition );
                        Logmessage( oPass.name + " に " + oPartition.name + " 作りマスター!!!!");
                    }
                }
               
                if ( mode == "move" )
                {               
                    var oObjects = FilterX3D( Selection );
                    if ( oObjects.count != 0 )
                    {
                        for ( var j=0; j<oObjects.count; j++ )
                        {
                            if ( !oPartition.IsMember( oObjects(j) ) )
                            {
                                oPartition.AddMember( oObjects(j) );
                                Logmessage( oObjects(j).name + " を "  + oPartition + " に入れマスター!!!!" );
                            }
                            else
                            {
                                Logmessage( "******** " + oObjects(j).name + " は既に "  + oPartition + " に入ってまスター!!!!" );
                            }
                        }                        
                    }
                }

            }
        }
    }   

   
//    ############################################################
    function FilterX3D( in_Objects )
    {
        var oX3DObjects = XSIFactory.CreateObject( "XSI.Collection" );
        for ( var j=0; j<Selection.count; j++ )
        {
            if ( Selection(j).IsClassOf( siX3DObjectID ) && !Selection(j).IsClassOf( siLightID ) )
            {
                oX3DObjects.Add( Selection(j) );
            }
        }
        return oX3DObjects;
    }
   
    function GetSelectedPassInPPG( )
    {
        var aPassNames = PPG.sPass.value.split( ";" );
        return aPassNames;
    }
}

function PickPartition( )
{
    var rtn = PickElement( siGroupFilter, "パーティションをピックして。", "パーティションだって言ってんだよオルァ" );
    var oPicked = rtn.Value( "PickedElement" );
    var button = rtn.Value( "ButtonPressed" );

    if ( button == 0 )
    {
        Logmessage( "モルァ" );    //button が 0 =ピックセッションがキャンセルされた場合
    }
    else if ( oPicked.type == "Partition" )
    {
        return oPicked;
    }
}

InspectObj( oP, null, null, siLock );








何に使うかと言いますと、


全Pass(あるいは PPG の中で指定した Pass)に対して

   新しいパーティションを作ります
   あるいは、新しいパーティションを作りながら、現在選択中のオブジェクトをブチ込んでいきます
   あるいは、既に存在しているパーティションに、現在選択中のオブジェクトをブチ込んでいきます



前回書いたのと似たような状況で使うのですが、つまり既に Pass 分けが進んでしまったシーンに対して、新規で追加したオブジェクトを全Passで特定のパーティションに入れていったりするものですね。 新規オブジェクトでなくても、Pass の運用方針が若干変わったりして、あーーこのオブジェクト、全 Pass で Background に戻してえよ、とかそういうのよくあるじゃないですか。 そういうときに使います。  Pass が1つや2つならともかく、6個や7個以上あるときは、Pass をひとつひとつ切り替えてブツをパーティションに追加していくとか、もうやってられません。



起動すると PPG が表れます。
Pattaysion

Target Passes の所に全 Pass がリスト表示されるので、対象にしたい Pass を選んでおきます。 起動時は、全 Pass が既に選ばれている状態になっています。

Target Partitions のところで、パーティションの名前を入力します。テキストフィールドに手打ちしてもいいし、Pick ボタンを押すとピックを促されるので、パーティションをクリックして下さい。するとそのパーティションの名前がフィールドに入ります。 
あるいは空っぽのままでもいいです。 その場合はボタンを押した後にピックを促されるのでそこでパーティションをピックします。

Create Partition ボタンを押すと、指定した Pass 以下に、テキストフィールドに入力した名前のパーティションを作成します。 作成するだけでオブジェクトをパーティションにブチ込んだりはしません。 Do not create ... のチェックがオンだと、すでにその名前のパーティションが存在していた場合はスキップします。つまり重複したパーティションは作りません。 このチェックがオフだと、重複していてもパーティションを作成します。 末尾に1 が付いた同名のパーティションを作成します(同じ名前は存在できないので)。

Create & Move Selection to Partition ボタンを押すと、パーティションを作成しながら、現在選択中のオブジェクトをそのパーティションに追加して行きます。この機能の方がよく使うでしょう。 Do not create ... チェックボックスの効果は上記と同じ=ダブってたら作らないか、ダブっていても作るかの指定です。
何も選んでない状態でこのボタンを押すと、パーティションが作成されるだけでオブジェクトが追加されることはありません(何も選んでないんだから当たり前)。 結果、となりの Create Partition ボタンを押したのと同じ挙動になります。


上に書いたように、パーティションの名前を入力しないままこれらのボタンを押した時は、ボタンを押した後にピックを促されるので、パーティションをひとつピックして下さい。するとそのパーティションの名前が全Pass(PPGの中で指定したPass)において対象になります。






大昔に同様の機能のスクリプトを書いてずっと使っていたのですが、いい加減設計が古かったので、つい最近このように書き直しました。書き直したてホヤホヤなのでなんか不具合ありそう。 っていうか、新たに作成されたパーティションを Inspect する機能とか付けるの忘れてた。 そのうちやろう。  あーー light の扱いも忘れてた。 現在、light はシカトされます。 いかんな。 light と普通のブツが混ざって選択されていた場合の対処を書かないと・・・・・。 現在はめんどくさくて light は無視するように書いている気がします。



Pass 分けな作業をしているお方がいらっしゃいましたら、テストしてくれると嬉しいッス。





最近ブログにナマなコードを書き逃げするだけで、ちゃんとまとめたりしてません orz
そのうち中身忘れるしなあ。もったいねえ。
まとめるかな。
来月だな。




.

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

2011年5月17日 (火)

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

先日書いた Partition.AddMember やら Group Layer Partition の取得やらを組み合わせたスクリプトを書きました。 


var oObjects = FilterX3D( Selection );    //    X3DObject 以外排除
if ( oObjects.count != 0 )
{
    LetMeBeWithYouPPG( oObjects );
}
else
{
    Logmessage( "ドルァ" );   
//    X3DObject がひとつもなかった場合
}

function LetMeBeWithYouPPG( oObjects )
{
   
//    PPG 作成
    var oP = XSIFactory.CreateObject( "CustomProperty" );
    oP.name = "あなたと一緒にいさせて( *゚д゚)♂";
   
   
//    パラメータ3つ
    oP.AddParameter2( "bPartitions", siBool, true );
    oP.AddParameter2( "bLayer", siBool, false );
    oP.AddParameter2( "bGroups", siBool, false );
   
   
//    PPG の UI 作成
    var oL = oP.PPGLayout;
    oL.AddSpacer(0,50);
    oL.AddRow( );
        oL.AddGroup( "", false, 25 );
            oL.AddItem( "bPartitions", "Partitions" );
        oL.EndGroup( );
        oL.AddGroup( "", false, 75 );
        oL.EndGroup( );
    oL.EndRow( );
    oL.AddSpacer(0,50);
    oL.AddRow( );
        oL.AddGroup( "", false, 37 );
        oL.EndGroup( );
        oL.AddGroup( "", false, 25 );
            oL.AddItem( "bLayer", "Layer" );
        oL.EndGroup( );
        oL.AddGroup( "", false, 37 );
        oL.EndGroup( );
    oL.EndRow( );
    oL.AddSpacer(0,50);
    oL.AddRow( );
        oL.AddGroup( "", false, 75 );
        oL.EndGroup( );
        oL.AddGroup( "", false, 25 );
            oL.AddItem( "bGroups", "Groups" );
        oL.EndGroup( );
    oL.EndRow( );
       
   
//    作成した PPG をモーダル表示、キャンセルじゃなければファンクションに飛ばす
    Inspect = InspectObj( oP, null, null, siModal, false );
    if ( !Inspect )
    {
        LetMeBeWithYou( oP, oObjects );
    }
    else
    {
        Logmessage( "ウルァ" );   
//    PPG がキャンセルされた場合
    }
}


function LetMeBeWithYou( oP, oObjects )
{

   
//    目的のオブジェクトをピック
    var rtn = PickElement( siObjectFilter, "一緒に居たい人をピックして。", "ピックしてよん。" );
    var oMasterObj = rtn.Value( "PickedElement" );
    var button = rtn.Value( "ButtonPressed" );

    if ( button == 0 )
    {
        Logmessage( "モルァ" );   
//    button が 0 =ピックセッションがキャンセルされた場合
    }
    else
    {
      
//    ピックされたオブジェクトの Partition, Layer, Group を取得
        var oOwners = oMasterObj.Owners;
        var oPartitions =    GetPartitionLayerGroup( oOwners, "Partition" );
        var oLayer =        GetPartitionLayerGroup( oOwners, "Layer" );
        var oGroups =        GetPartitionLayerGroup( oOwners, "#Group" );   
   
       
//    Partition 一緒にいさせて( *゚д゚)♂
        if ( oP.bPartitions.value )
        {
            for ( var i=0; i<oObjects.count; i++ )
            {
                for ( var j=0; j<oPartitions.count; j++ )
                {
                    if ( !oPartitions(j).IsMember( oObjects(i) ) )
//既にメンバだった場合に止まってしまうのを回避
                    {
                        oPartitions(j).AddMember( oObjects(i) );
                    }
                }
            }   
        }
       
       
//    Layer 一緒にいさせて( *゚д゚)♂
        if ( oP.bLayer.value )
        {
            
//    Primitive オブジェクトをピックできてしまったりするんだが MoveToLayerコマンドではエラーが出るので一応トラップ
            try
            {
                MoveToLayer( oLayer, oObjects );   
// Layer は OM 効かない。コマンドのみ
            }
            catch (e)
            {
                Logmessage( "Layer でオルァ" );
            }
        }
       
       
//    Group 一緒にいさせて( *゚д゚)♂
        if ( oP.bGroups.value )
        {
            for ( var i=0; i<oGroups.count; i++ )
            {
                oGroups(i).AddMember( oObjects );   
//    Group は Partition と違って既にメンバだった場合でも何もしなくて桶
            }

        }
    }
}


//    以下ヘルパーファンクション

//    Partition, Layer, Group を返すファンクション

function GetPartitionLayerGroup( oOwners, TargetType )
{
   
//    カラのコレクションを作り、指定のタイプのものを見つけたらコレクションにブチ込み、最後にそれを返す
    var oHogehoges = XSIFactory.CreateObject( "XSI.Collection" );
//コレクション(入れ物)用意
    for ( var j=0; j<oOwners.count; j++ )
    {
        if ( oOwners(j).type == TargetType )   
//oOwners のうち、指定されたタイプのものだったらコレクションに追加
        {
            oHogehoges.Add( oOwners(j) );
        }   
    }
    return oHogehoges;   
//    コレクションを返す
}


//    X3DObjects のみを返すファンクション

function FilterX3D( oObjects )
{
    var oX3DObjects = XSIFactory.CreateObject( "XSI.Collection" )
    for ( var j=0; j<oObjects.count; j++ )
    {
        if ( oObjects(j).IsClassOf( siX3DObjectID ) )
        {
            oX3DObjects.Add( oObjects(j) );
        }
    }
    return oX3DObjects;
}





何をするスクリプトかと言いますと、現在選択中のオブジェクトの Layer Group Partition をピックしたオブジェクトに一致させるというものです。

シーンを作り進めて、Layer も Group も Partition もある程度できてしまっているとするじゃないですか。 そこに、何らかの都合で新しいオブジェクトが加わった場合などに使うことを想定しています。 オブジェクトが差し替えになった場合なども典型的な場面ですね。 こういう時に、その新規オブジェクトに対して、これまでこのシーンを構築してきたルールに従って正しい Group や Partition に入れていったりするのって、すんげえ大変じゃないですか。

でも、その新規オブジェクトと同じ法則で分類して良いオブジェクトが既にシーンの中にあったとしたら、Group Layer Partition は全部そいつに従えって指定してあげることができればラクですよね。 そういうスクリプトです。



こんな状態。
Letmebewithyou1
現在、sphere1~4 を選択しています。
この4つは、全ての Layer Group Partition において、赤矢印で示した cube 様と一緒に居たいとします。


なので、この4つを選択した状態でスクリプトを起動します。
すると PPG が立ち上がります。
Letmebewithyou2

今回は全ての Layer Group Partition において一緒に居たいので全部チェックオンにしますが、Layer は一緒でなくてよいとかそういう時はチェックを外します。


OKを押すとピックを促されます。
ここで cube 様をピックします。


すると、Layer Group Partition が cube 様と一緒になります。
Letmebewithyou3

以上。


Layer は1つにしか所属できないわけですが、cube 様の Layer に入ります。

Partition は全 Pass において、cube 様と同じ Partition に入ります。 当たり前ですが全Pass という所が重要です。

Group は複数に所属できるわけですが、cube 様が所属する Group に全部入ります。 ただし、cube 様とは関係なく sphere 達が独自に加入していた Group があった場合、その Group から追い出されるわけではありません。

Light Partition のことはあんまり考慮してませんでした。 ただ、ライトを選択した状態でライトではないオブジェクトをピックしても、別にエラーで止まらないようなので、このまんまでもいいかなと思います。 その他、例えば null など Partition に入れないオブジェクト( Layer や Group には入れる)に対して Partition の一致を指示した場合などでも、特にエラーで止まることなく無視されるようなので、ひとまず放置します。





まあいつものごとく、ベータ版のようなものです。 どなたか使ってみて、不具合とか改良案とか教えてくれませんかね。



Partition が一番使いますね。 Pass分けがかなり進んでしまってから、Pass をいちいち切り替えて Partition に追加とか、手でやってらんないですからね。 

最近はこれ以外にも似たような Partition がらみのスクリプトをちょこちょこ書いてます。





ごきげんよう。




.

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

2011年5月15日 (日)

さわやかな朝に Group Layer Partition。

日曜日の朝。

自然と目が覚めてみたれば、時刻は6時半でした。

二度寝するにはもったいないほどの、さわやかな朝でした。

自室の窓からは、久しぶりに綺麗に富士山が見えました。

Img_0012

こんな気持ちのいい朝は、スクリプティングです。



以前も Group と Layer と Partition の判別の話を書いたことがありましたが、XSI 七ちゃん以降であれば、もっと単純にできるかなと思って書いてみたものです。 目覚ましにちょうど良い頭脳労働でした。20分くらいで書きました。すっかり目が覚めて、5月の朝の風を満喫しています。




for ( var i=0; i<Selection.count; i++ )
{
    if ( Selection(i).IsClassOf( siX3DObjectID ) )   
//    対象は3Dオブジェクトのみに限定
    {
        var oOwners = Selection(i).Owners;   
//    Owners を取得
        var oPartitions =    GetPartitionLayerGroup( oOwners, "Partition" );
   //ファンクションに飛ばしてパーティション取得
        var oLayer =        GetPartitionLayerGroup( oOwners, "Layer" );
        //ファンクションに飛ばしてレイヤ取得
        var oGroups =        GetPartitionLayerGroup( oOwners, "#Group" );
        //ファンクションに飛ばしてグループ取得

        logmessage( Selection(i) + " ##############" );
        logmessage( "Partitions : " + oPartitions );
        logmessage( "Layer : " + oLayer );
        logmessage( "Groups : " + oGroups );
    }
}

 

function GetPartitionLayerGroup( oOwners, TargetType )
{

    //    カラのコレクションを作り、指定のタイプのものを見つけたらコレクションにブチ込み、最後にそれを返す
    var oHogehoges = XSIFactory.CreateObject( "XSI.Collection" );
//コレクション(入れ物)用意
    for ( var j=0; j<oOwners.count; j++ )
    {
        if ( oOwners(j).type == TargetType )
    //oOwners のうち、指定されたタイプのものだけをコレクションに追加
        {
            oHogehoges.Add( oOwners(j) );
        }   
    }
    return oHogehoges;
    //    コレクションを返す
}





一応ちゃんと動いているように見える。
A

適当に3Dオブジェクト(複数可)を選んで実行します。
するとそのブツが所属する Group Layer Partition がログされます。


スクリプティング的には、Owners を使っているところが味噌になるでしょうか。 オブジェクトには Owners というプロパティがあって、「自分を所有しているもの」が入ったコレクションが返ってきます。 「自分を所有しているもの」とは、言い換えれば自分が所属している先ということになります。 従って所属する Group Layer Partition などを調べるには最適の方法だと思います。 

あとは Owners の中にごっちゃになって入っている Group Layer Partition その他の中から、目的のものをより分けられれば良いだけ。 昔と違って Partition がオブジェクトとして独立したので、単に siType を Layer #Group Partition のいずれかに指定してやるだけで、簡単に分けられました。


しかし、本来効くはずの Owners.Filter ( "Partition" ) などが効きやがりません。 Owners の戻り値は ProjectItemCollection なので Filter メソッドが効くのですが、siType の部分に Partition や #Group と入れると空っぽのコレクションが返ってきてしまいます。これ不具合だと思います。誰か追試して下さい。


ともかく、この Group Layer Partition より分けは汎用的に何にでも使えそうです。
まずは前から考えていたアレを作ってみよう。
いや、大したものではないのですが。




ではごきげんよう。






.

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

2011年5月14日 (土)

Partition.AddMember の罠。

長らく続いたモデリング作業も終盤を迎え、その後はレンダリングの必要があるわけでして、溜まったモデリング系のスクリプトも整理しないうちにレンダリングに関わるスクリプトも書かざるを得ないという日々であります。 いや、そんなに大したものは書いてませんよ。 パーティション分けのツールをほんのちょっと書いているだけです。


で、パーティションのスクリプトで気づいたことを書きます。


XSI 七ちゃんの時からだと思うけど、Partition オブジェクトが独立し、色々楽になりました。 Partition ってもともと Group や Layer と同じ種類のモノ( siGroupType )なのですが(アイコンも Group や Layer と色が違うだけで形は同じ)、独立した Type を持つようになったため取得するのも超楽になったし、Group に有効なメソッドやプロパティが Partition にも使えるようになったりしました。


がしかし。
落とし穴が。

いや、そんな大した落とし穴でもないんですが。
でもこれ気づくまでにだいぶ時間かかった。
故にスクリプトが上手く動かなくてかなり時間をロスしました。


こんな状態です。
1
Partition_A には Hage1~3 が入っています。
Partition_B の方には Hage4~6 が入っています。

Partition_B に入っている Hage 4, 5, 6 を Partition_A に移したいとします。
そこでこういうスクリプトを書きます。

 var oPartition = Dictionary.GetObject( "Passes.Default_Pass.Partition_A" );
 oPartition.AddMember( Selection );


1行目は、安直な方法でパーティションオブジェクトを取得しているだけです。名前がフルパスでわかっているなら GetObject で取得しちまうのが簡単です。 今回この行は重要ではない。

大事なのは2行目なんですが、1行目で取得したパーティションオブジェクト( oPartition にバインドされている)に対して、AddMember の呪文を唱えています。 そのまんまですね、パーティションにメンバを追加する、つまりこのパーティションにこのオブジェクトを入れろと指令しているわけですね。 カッコの中は Selection = 現在選択中のブツ です。 つまり、oPartition の中に現在選択中のオブジェクトを入れろと言っているわけです。

Hage4, 5, 6 を選んだ状態でこのスクリプトを実行すると、
2
こうなります。 問題ありません。


次にたった今移動させた Hage4, 5, 6 を Partition_B に戻して、元の状態にします。

次に、Hage1, 4, 5, 6 を選択した状態で同じスクリプトを走らせてみます。
Hage1 のみは、既に Partition_A に入ってしまっていますね。この状態で実行すると、
3
あれっΣ(゚Д゚)

この画像は、スクリプト実行後です。
4, 5, 6 は Partition_B に入ったままになってますね。移動してくれないんですよ。


どうやら、移動先のパーティションに既に入ってしまってるものが1つでも含まれていた場合、Partition.AddMember を使うと移動が行われないようなんですね。 これに気づくまでにすんげえ時間がかかりました。 


選択中のオブジェクトを全 Pass で特定のパーティションに移動させるみたいなスクリプトを書いて、わあ便利だ俺は天才などと思っていたら、ちゃんと移動していないことが多々あることを発見し、コードを見直してもどこも間違ってないように見え、あーだこーだといじくったりモニタを2個くらい窓から遠投しているうちに選択しているブツによって結果が変わることを発見し、観察した結果、どうやら上に書いたことが原因らしいと思われる、という状況です。

観察した結果そうであるらしい、というだけですよ。 マニュアルに書かれてませんもの。 AddMember のヘルプページ見ても 「すでにメンバであるオブジェクトが含まれていると実行されません」とかなんとか、書いてないですから。 ちゃんと書いとけドルァ


そして Partition ではなく、Group に対して AddMember を実行した時はちゃんと動くんです。既にその Group に入ってしまっているものが移動対象に含まれていても正しく AddMember が実行され、既に入っているそのオブジェクトも含めた全てがちゃんと Group のメンバになってくれます。 Group に対してはちゃんと動くのに、なぜ Partition に対しては動かないのか。 ノルァ

思うにですね、Group と Partition の決定的な違いは、ブツは複数の Group に入ることができるけど、Partition は1個しか入れない、という部分ですよね。 上記の不審な挙動はこの違いから来ているんじゃないかと思っているんですが、どうですかね。 そして AddMember メソッドはもともと Group 用に作られたものであり、Group とは違ってブツが所属できるのは1個だけという Partition の特徴は考慮に入れられてない設計なんだろうと思うんですよ。 でも XSI が七ちゃんになって Partition オブジェクトが独立した時に、Partition オブジェクトにも安直に Group と同じスクリプトインターフェースをサポートさせた結果このような挙動になっているのではないか、と疑っているのです。 どうなんですか嘔吐デスクさん。 いや、これは嘔吐デスクに買って頂く前の話なので、犯人は Avid 傘下の Softimage さんですね。

ヘルプのページを見ても、Partition.AddMember に行くと Group.AddMember に飛ばされますからね。明らかに Group 用の AddMember を流用しているとでも言うか。 だから Partition 用に独自の AddMember があればいいんだと思うんですよ。 後述しますがコマンドでやると問題なく動くわけで、オブジェクトモデルで AddMember した時と挙動が違うのはやはり不具合と言うべきでしょう。 直してくれ。 こんなちょっとのことで、自作のツールが動かなくて原因解析に何時間も使っちまうんだよ嘔吐デスクさん。



ってことでですね、回避策のひとつとして、オブジェクトモデルを使うことをあきらめ、つまり Partition オブジェクトに対して AddMember の呪文を唱えることをやめて、昔ながらのコマンドでやっちまうことにしてみます。
4


var oPartition = Dictionary.GetObject( "Passes.Default_Pass.Partition_A" );
MoveToPartition( oPartition, Selection  );


1行目は全く同じ。
2行目で、MoveToPartition コマンドを使っています。

これを実行すると、Hage1 という既に Partition_A に入っちまってるオブジェクトが含まれているにも関わらず、正しく全員 Partition_A に入ってくれます。 ふう。 一応解決。


<余談>
コマンドを使っているということは、フルパスで名前(文字列)を直接指定して事を進行させるということなので、パーティションをオブジェクトとして取得する必要はありません。なので、パーティションをオブジェクトとして取得している1行目は今回、本質的には必要ではありません。2行目の oPartition の所に、1行目の "Passes.Default_Pass.Partition_A" を書けばいいだけです。  じゃあなぜ必要のないコードを書いているのかと言うと、今回の場合は、最初のスクリプトと同じ構造(まず取得し、取得したものを使って何かをやる)に見える方が説明上わかり易かろうと思ってこうした、というだけのことです。

さらに言えば、コマンドではオブジェクトではなく「名前」を使うわけなので、2行目の oPartition は、1行目で取得したパーティションオブジェクトを  MoveToPartition コマンドに渡しているわけではなく、oPartition の名前(文字列)を渡しているだけということになります。 oPartition は厳然たるオブジェクトですが(1行目で取得しているので)、コマンドに渡す際にはオブジェクトとして渡されるわけではなく、名前(文字列)として渡されているだけだということです。 oPartition.fullname という表記が省略されて oPartition と書かれていると考えた方が良いでしょう。

だからと言ってコマンドに対して取得したオブジェクトを食わせるのは間違いだとか無駄だとか言っているのではありません。こんなん、いくらでもやりますからね。なんら間違った手法ではありません。 ただ、渡されているものはオブジェクトではなく文字列なんだ、という意識があった方が、スクリプティングそのものの理解には役に立つと思います。

さらに言うと、こんなこと、マニュアルに書かれていたわけではありません。たぶん。 今までの経験と、ネット上で色々読んだ話などから、「ああ、コマンドというものは文字列を食っているんだなあ」とだんだん理解してきた、というのが本当のところです。 なのでもしかしてとんでもなく間違ったことを言っているかもしれません。 その場合、どうか指摘して下さい。
</余談>




さて、コマンドで問題なく実行できたのでよしとしますが、しかも七ちゃん以前はこの方法しかなかったわけで、これ以上突っ込む必要は全くないと言えばないのですが、可能な限りコマンドを廃してオブジェクトモデルを使おうとしている身としては、MoveToPartition コマンドを撲滅したいと思うわけです。 コマンドはログが残ってうざったかったり(それが良かったりもするんですが)、実行も遅いですからね。

ということで、こうしてみました。
5

var oPartition = Dictionary.GetObject( "Passes.Default_Pass.Partition_A" );
for ( var i=0; i<Selection.count; i++ )
{
    if ( ! oPartition.IsMember( Selection(i) ) )
    {
        oPartition.AddMember( Selection(i) );
    }
}


1行目は同じ。 目的のパーティションをオブジェクトとして取得しています。

その後、現在選択しているものをループして、既に目的のパーティションのメンバになっているかどうかひとつずつ調べる。 メンバでなければ、メンバにする。

IsMember は Partition や Group に効く呪文ですね。まあこれも上の書いたのと同じく元々は Group 用の呪文だと思います。 でも Partition に使っても今のところ不具合は見当たらない。   oPartition.IsMember( Selection(i) ) というのは、パーティション(oPartition)にとって、ループ中のブツ( Selection(i) )が、メンバであるのかどうかを調べるという意味になります。 メンバであれば true を返し、メンバでなければ false を返します。 

AddMemberを使ったとき、すでにメンバである人が含まれているとちゃんと実行されないのであれば、しかたない、ひとりひとりメンバかどうかを調べて、選別してから AddMember に渡してあげようということです。 めんどくさいですね。なんとかしなさい嘔吐デスク様。


ま、こんな余計なコードを書いてスクリプトは複雑になったように見えますが、実行は速いです。オブジェクトモデルですから。 数個のオブジェクトに対して実行してもあまり速さはわからないかも知れませんが、50個や100個に対して実行すると、MoveToPartition コマンドを使うよりもあからさまに速いのがわかります。 そして、そもそも手でやるのでははなくスクリプトでやろうとしていることなんだから、基本的には数が多いわけです。50個や100個を扱いたいからスクリプトを書いているわけです。 だから実行の速さは正義です。


ということで、コマンドを廃してオブジェクトモデルだけで望みの挙動になるスクリプトが書けました。
でもめんどくさい。 
メンバが既に含まれているかどうかに関係なく AddMember が使えるようになればそれで済むこと。
Group に対する AddMember の挙動が、Partition に対しても同じになればそれで済むこと。
嘔吐デスク様、なんとかして下さい。



ごきげんよう。



.

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

2011年5月13日 (金)

XSI男Tシャツ発送完了。

ふう。 やっと送りますた。
お待たせしてほんとにすいません。


結局、俺を含めてですが延べ33名の方々が、合計57枚購入してくれました。
57枚! 
皆さん、全力で感謝します
ありがとうございましたっ (゚∀゚;)




ですが、取りまとめというか、振り分けが思ったより大変で、時間食ってしまいました。


送られてきたTシャツは、タグの所にサイズは書いてあるんだけど、色の名前が書いてないんですね。 なので、WEB サイトの色見本を見て色を特定し、あらかじめまとめてあったエクセルの表と照らし合わせて、何色の何サイズは誰様、という振り分けを1枚1枚していたので、なかなか大変でした。

えーーーとこの色は、ロイヤルブルーだな、ライトブルーじゃないよな、ええと、サイズは L で、注文者はエクセルの表を見ると、ええと、ワタナベさん。 とかなんとか。



宛名は手書きしてられないので紙にプリントアウトしたのですが、それを1枚1枚貼るのもなかなか手間でした。テープ型の糊が活躍しました。

また、Tシャツのビニール袋の端っこを折らないと封筒に入らないのです。これも手間でした。


そんなことを深夜や、朝起きてから出社するまでのわずかの時間に少しずつ進めて、何日だかかかっちまいました。


Img_0014

ずらりと並んだTシャツ入り封筒。 なかなか壮観ですよ。




大変な手間ではありましたが、実に楽しかったですね。


あ。 この人、見かけによらずけっこうでかいサイズだな。 けけけけ。

え。 あんたLで大丈夫なの。XLの方が良かったんじゃないの。 けけけけ。

お。 この人とこの人、あの会社で席が背中合わせの二人じゃないか。二人して同じ色のTシャツ頼んでるよ。同時に会社に着てきたりしたらどうなるのかな。 けけけけけけけ。


面識の無いお方はともかく、知っている人はもちろん顔を思い浮かべながら封筒に入れていくわけでね。 いやあ楽しかった。 けけけけけけけ。



でもまだ終わりじゃありません。
肝心の寄附をまだ実行していません。
俺自身ちょいと仕事が忙しくて、なかなか進められていません。
近いうちに実行して、ちゃんと会計報告しますので、もう少しお待ち下さい。
Tシャツは明日、というか明けてもう今日ですね、今日届くと思います。たぶん。



平積みにしたTシャツ入り封筒。
Img_0016

クロネコヤマトの営業所に持ち込んでメール便で発送しました。
80円のメール便で送れるのは、厚みが1センチまでです。
写真を見るとわかるように、封筒の厚みは明らかに1センチを超えています。XLを入れた封筒などは、2センチも超えているくらいです。

でも今回頼んだTシャツ屋さんの話によれば、XLをA4封筒に入れて80円で何度も送った実績があるとのこと。

営業所には段ボールに入れて持ち込みました。 そしてSとか160とか小さめのサイズのTシャツが入った封筒が、箱の中で上の方に来るような配置で入れておきました。

で、営業所のおっさんが 「うーん、1センチは超えているねえ」 と言うんだけど、俺はしらばっくれて 「いやいや、以前同じものを80円で送れたよ」 とかテキトーなことを言いながら箱の中で一番上にある封筒を出し、厚みを計る専用の道具で封筒を挟み込んで、なにせ中身はTシャツだから若干は押されて圧縮されるので、まあまあ1センチに収まったと言えなくもない、という状態になりました。 「他の封筒は?」 「全部同じものです」 「そうかい。わかった、それでいいよ。 じゃあ80円 x 枚数ね」 ってな感じのやり取りで、俺はさっさと金を払ってしまい、明らかにサイズオーバーな封筒も同じ送料で受付してもらって営業所を去りました。
 
おじさん、たぶんサイズオーバーなのはわかった上でオマケしてくれたんだと思う。 福島復興支援Tシャツだと聞いて、へえ、若いのに感心だねえ、とか言ってくれてたので、ちょっと好感を持ったのでしょう。若くないけど。   おじさんありがとね。



平積みTシャツがどれくらいの高さになっているかわかるよう、比較対象に、ギターを置きました。
Img_0021

6フレットくらいの高さですた。





.

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

2011年5月 6日 (金)

貴様、ダブってるなら失せろ。

クラスタにマテリアルを与えているうちに、同じマテリアルをうっかり複数クラスタに与えてしまうことってあるじゃないですか。

と言うよりも、そのマテリアルを持つクラスタはすでに存在しているから、クラスタにポリゴンを追加すりゃいいだけなのに、ポリゴンにまた同じマテリアルを与えることによってうっかり新規クラスタとして作成してしまうことってよくあるじゃないですか。

そんなことして無駄に増えていったクラスタを整理するスクリプトを書いてみたのですが、一応動いているように見える。


if ( Selection.Filter.name == "object" )
{
    for ( var i=0; i<Selection.count; i++ )
    {
        if ( Selection(i).IsClassOf( siX3DObjectID ) )
        var oClusters = Selection(i).ActivePrimitive.Geometry.Clusters;
        MergeClusters( oClusters );
    }
}
else
{
    Logmessage( "ドルァ" );
}

function MergeClusters( oClusters )
{
    var oMatCol = XSIFactory.CreateObject( "XSI.Collection" );
    oMatCol.Unique = true;   
//    これ重要
    var oMatClsCol = XSIFactory.CreateObject( "XSI.Collection" );

    //    GridParameter にぶち込むための 「クラスタ&そのマテリアルコレクション」 「マテリアルのコレクション」 作成
    for ( var i=0; i<oClusters.count; i++ )
    {
        if ( oClusters(i).type == "poly" )
        {
            var oMat = GetClusterMat( oClusters(i) );
            if ( oMat )
            {
                oMatClsCol.Add( oClusters(i) );
                oMatClsCol.Add( oMat );
                oMatCol.Add( oMat );
            }
        }
    }

    //    oMatClsCol のイメージ
    //    クラスタオブジェクト1, そのマテリアル, クラスタオブジェクト2, そのマテリアル......
    //    Cls1, Cls1.mat, Cls2, Cls2.mat, Cls3, Cls3.mat .....

    //Logmessage( oMatClsCol.count + " : " + oMatClsCol );
    //Logmessage( oMatCol.count + " : " +  oMatCol )

   
   

    //    GridParameter 作成   2次元配列みたいなもん?
    //    行=マテリアル  列=そのマテリアルを持つクラスタ

    var oP = XSIFactory.CreateObject("CustomProperty");
    var oGridParam = oP.AddGridParameter("Grid") ;
    var oGridData = oGridParam.value;
    oGridData.RowCount = oMatCol.count;
    oGridData.ColumnCount = oMatClsCol.count/2;

    //    ここで Row (行)の数(=マテリアルの数)は先に確定
    for ( var i=0; i<oMatCol.count; i++ )
    {
        oGridData.SetRowLabel( i, oMatCol(i).name );
    }

    //    ルックアップテーブル作成
    ClsCounter = 0;
    for ( var i=0; i<oMatClsCol.count; i+=2 )
    {
        var oCls = oMatClsCol(i);
        var oMat = oMatClsCol(i+1);
        for ( var j=0; j<oGridData.RowCount; j++ )
        {
            RowLabel = oGridData.GetRowLabel( j );
            if ( RowLabel == oMat.name )
            {
                oGridData.SetCell( ClsCounter, j, oCls );
  // デバッグ時、この行と次の行を入れ替える
                //oGridData.SetCell( ClsCounter, j, oCls.name );
                //    → GridParameter を目で見るため。 かつ、下の InspectObj を有効にする

                ClsCounter++;
            }
        }
    }

    //InspectObj( oP )

    //    テーブルの Row ごとに(=マテリアルごとに)処理
    for ( var i=0; i<oGridData.RowCount; i++ )
    {

        //    セルの値(クラスタ)をアレイにぶち込み、さらに空白削除して左詰めにした新規アレイを再構築
        var aSafeArray  = oGridData.GetRowValues( i );
        var aValues = new VBArray( aSafeArray ).toArray() ;
        var aClusters = new Array( ); 
//空白削除用の新規アレイ
        for ( var j=0; j<aValues.length; j++ )
        {

               if ( aValues[j] != null )  //元のセルから取った値が null じゃなかったら新規アレイに追加という。
            {
                aClusters.push( aValues[j] );
            }
        }

        if ( aClusters.length != 1 )
        {
            var oDestinationCluster = aClusters[0];
            for ( var j=1; j<aClusters.length; j++ )
            {
                var oSub = aClusters[j].CreateSubComponent( );
                DeletedClusterName = aClusters[j].name;
                DeleteObj( aClusters[j] );
                var oUpdatedCluster = SIAddToCluster( oDestinationCluster, oSub )(0);
                Logmessage( DeletedClusterName + " --> " + oDestinationCluster.name + " に統合しますた!! (;゚∀゚)" );
            }
        }
    }

}

function GetClusterMat( oCluster )
{
    for ( var j=0; j<oCluster.LocalProperties.count; j++ )
    {
        if ( oCluster.LocalProperties(j).IsClassOf( siMaterialID ) )
        {
            return oCluster.LocalProperties(j);
            break;
        }
    }
}




オブジェクト(複数可)を選んで実行します。 ポリゴンとかクラスタを選んでいるとダメです。オブジェクト選択モードでないといけません。

ポリゴンクラスタを探し、見つかったポリゴンクラスタからマテリアルを探し、マテリアルがダブっていたらクラスタを統合し、抹殺します。






たとえばこの状態。
Mergeclsbymat

この sphere には、ポリゴンクラスタが5個ありますね。
そのうち、Cls4 は独自のマテリアルを持っていませんでした。よって Cls4 は無視します。
Cls4 以外のクラスタが持つマテリアルを比較します。
すると Cls3 と Cls5 が mat3 という同一のマテリアルを持っていることがわかりました。


この状態でスクリプトを実行すると、
Mergeclsbymat2

Mergeclsbymat3

こうなります。



1枚目の画像でエクセルのシートのようなものが表示されている PPG がありますが、これがスクリプトの中で内部的に使っているルックアップテーブルです。 スクリプトの中のコメントに従って書き換えると、画像のように表示できます。デバッグ時に表示させて、正しく動いているかどうかの検証に使っていたものです。脳味噌の中でこねくり回しても俺はすぐ混乱してしまうので、XSI の GridParameter を使って可視化しているわけですね。 このように、かき集めたクラスタとそのマテリアルをこの表に一度書き込んでしまい、次に表の頭から順に比較し、処理をしています。

もっと上手い方法もあると思う。アルゴリズム的にあまり美しくない気もする。無駄なことやってる部分もあるでしょう。でもまあ、一応望んだ挙動になっているのでOKです。




マテリアルの中身を比較したりはしません。つまり、例えば mat1 と mat2 の RenderTree 構成や各シェーダのパラメータが全く同一だったとしても、mat1 と mat2 という風にシーン内に別マテリアルとして存在している以上、中身が同一でも別マテリアルと見なします。




クラスタ関係のスクリプトはいずれ整理して、一群のツールセットにしたくなってきました。クラスタマテリアルをたくさん使わざるを得ないタイプのモデリング作業をしていると、この辺のスクリプトが必須です。手でやってたら気が狂います。





ごきげんよう





.

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

« 2011年4月 | トップページ | 2011年6月 »