2011/11/29

ICE : 配列とセットの違いを理解する

ICEを構築していると、いろいろな壁にぶち当たります。
そんな、そびえ立つ大きな壁のひとつとして、配列とセットという概念があります。

配列とセットの違いは、ICEを攻略する上で、非常に重要な概念となるので、覚えておく必要があります。

PointCloud Grid 3x3
Get -> Primitive -> Point Cloud -> Grid
から、3x3のPoint Cloud Gridを作成して、以下のICE Treeを繋ぎます。

上の黄緑の流れは、2012から搭載された、Build Array from Setノードを使用しています。
内部的には、セットから配列へ変換するノードです。
結果、配列で表示されています。


対して、下の黄色の流れは、普通にPointPositionを表示しています。
こちらは、セットです。


それぞれをカスタムICEアトリビュートのself.a、self.bと、二つにデータを格納しています。


Show Valuesのポイント
データを確認したいときは、ノードの線を右クリックして、Show Valuesでのプロパティの設定を開いてOKボタンで確認します。
ですが、今回のポイントは、このプロパティの一番下の文章です。

Labelに#を入れると、IDで置換します。

となっていますので


#:

とLabelに入力したものが、上の図です。
黄緑配列の値は、こんな感じで表示。
[ ] の中の数字が増えていっています。

0[0]:-4, 0, -4
0[1]:-4, 0, 0
0[2]:-4, 0, 4
    .
    .
    .

一方黄色セットの表示は、

0: -4, 0, -4

1:-4, 0, 0
2:-4, 0, 4
    .
    .
    .

と、[ ]が無い状態ですが、一番左の数字が増えていっています。

この違いは、一体なんなのか。

これを理解するには、IDという存在がキーポイントとなります。IDというのは、オブジェクトのポイントなどの頂点番号の事です。

3x3のPoint Cloud Gridは、9個のポイントから成り立っています。その頂点番号は

0, 1, 2, 3, 4, 5, 6, 7, 8
右上のポイントを選択
といったように、0から始まる番号がポイントひとつひとつに割り振られています。

試しに右上のポイントをタグ選択して、Selectを見てみます。

6番が選択されています。丁度、黄色セットの表示では

6:4, 0, -4

となっており、6つ目の要素であること確認出来ます。
ポジションの値からも確認することが出来ますね。

つまり、セットとは、IDというオブジェクトの頂点番号とセットのデータということになります。

一方、配列のほうは、IDが0の場所に、9個のデータが一気に格納されています。

ここが決定的に違うところなのです。

詳しく見ていきましょう。

PointPositionへ接続
配列は挿せず、セットは挿せる
上のツリーから、Set Dataに Self.PointPositionを追加し、そこへ、配列のaというデータと、セットのbというデータが挿せるかどうか、試してみます。

IDを持たない配列aの場合、PointPositionへ接続しようとしても、拒否されてしまいます。
PointPositionは、IDを必要とするICEアトリビュートのようです。

変わって、セットbの場合は、ID情報があるので、素直に接続することが出来ます。


配列にIDを割り振りPointPositionへ接続
配列aのようなデータをPointPositionへ接続するには、IDを割り振るという作業が必要です。

Get Point IDSelect in Array ノードをこのように繋げば、PointPositionへ挿すことが出来ます。

このコンボは、非常に良く使うので、覚えておいて損は無いでしょう。

さらに、詳しく見ていきましょう。

違う大きさのCubeA、B
良くある問題で、このオブジェクトと同じ形状にしたいのになんで接続出来ないんだろう・・・?
ということが、たびたびあるかと思います。

違うオブジェクトからデータを持ってきたときに、接続出来ないことが良くあります。


これも実は、IDの問題なのです。


別のPointPositionのデータが繋がらない
Bの形状を、Aと同じにしたい。と考えた場合、BにICE Treeを作り、誰でも最初はこういう風に挿そうと思うものです。

今まで説明してきたセットのあり方を考えると接続出来るのでは?と不思議に思うのですが、実は、IDというものは、オブジェクトごとに固有に持っているもののようです。

この状態ですと、AのIDを持ったPointPositionのデータを、BのIDのPointPositionへ無理やり持ってきているようなものです。

ですので、考え方として、AのPointPositionを、一度IDを持たない配列データにし、BのIDを割り振り直し、BのPointPositionへ挿すという風にします。

Aのセットを配列にし、BのIDを割り振る
Build Array from Set で、配列データへ変換した後、Get Point ID と Select in Array ノードで、BオブジェクトのIDを割り振るといったことをすれば、挿せるようになります。




このように、配列とセットを理解するだけで、かなり出来ないことが出来るようになるはずです。

実を言う僕も、触り始めて半年くらい良く分かりませんでした・・・。
他人が繋いでいるのをなんとなく見ていて、「ああ、なるほど!こういう風にやればさせるのか!」と思っていたのですが、なんで?というのが、拭いきれませんでした。

完全に理解できたのは、Show Valuesで、Labelに#を入れて見た瞬間でした。

なるほどなぁ。こう違うんだ。
とね。

読んでくれてありがとう。


2011/11/22

Python : シェーダがマテリアルに繋がっているか調べる

シェーダがマテリアルと繋がっているのかいないのかを調べる関数です。

7.5辺りから、Render Tree上でリフレッシュしても繋がっていないものが残るようになりました。
Material.GetAllShadersを使用すると、繋がっていない、ごみシェーダを取得してしまいます。

なので、本当に使われているのか?という判定に使えます。
GetShaderParameterTargetsを使います。
このメソッドは、シェーダのアウトプットに繋がっているパラメータコレクションを返してくれる便利なものです。


from siutils import sisel # Selection
from siutils import log  # LogMessage

# シェーダがマテリアルに繋がっているかを調べる
def IsConnect(oShd):
 # シェーダのアウトプットのパラメータコレクションを取得
 oShdTgt = oShd.GetShaderParameterTargets("")
 # パラメータコレクションの数が0(すなわち繋がっていない)の場合は、False
 if not oShdTgt.Count: return False
 for oTgt in oShdTgt:
  # パラメータの親、つまりシェーダかマテリアルかを調べる
  if oTgt.Parent.Type == "material":
   # マテリアルなら、繋がっている
   return True
  else:
   # マテリアルじゃないなら、再度そのシェーダのアウトプットを再帰的に調べる
   return False or IsConnect(oTgt.Parent)




2011/11/18

Chain Generator

Twitterにて、#ICEドリルが始まりました。
ICEの質問の投稿があって、それをみんなで答えていたので、これはハッシュタグつけて、みんなでやったら面白いんじゃないかと思い、始めてみたのがきっかけです。

このICEドリルの参加に制限はひとつだけです。

行き詰ったり、こういうのどう繋げばいいのかふと思ったり、自分はこういう風に繋いでいるけどほかの人はどう繋ぐんだろう?とか、いろいろな動機があると思いますが、その質問に、#ICEドリルとハッシュタグを付けるだけで、参加出来るものです。

ただ、誰か質問しているのにも関わらず、さらにその上から質問を投げるのは、禁止します。

と前置きはいいとしまして、そのICEドリルから、Chain Generatorが産まれました。

今回は、そのプラグインの紹介です。

ICEで、鎖を簡単に作れます。
動画はこちら。



コンパウンドは、こちらです。
bit.ly/vJA7Wu

コンパウンドをICE Treeにドラッグアンドドロップで使用可能になります。

Chain Generatorプロパティ
動画は、1.0のバージョンのものですが、現在は、1.1になっています。

では、機能の紹介です。

Curve Name
一番上は、curveオブジェクトを入力します。
そこに、Pointが発生します。
Get Dataノードで、カーブオブジェクトの名前を挿すことも出来ます。

Distance
この値は、PointとPointの距離です。
大きければ、離れるし、小さいと密着した鎖が作れます。
大体鎖オブジェクトの大きさを入れればOK。

Accuracy
これは、精度です。
ICEノードには、カーブ上の位置を等間隔にするものが無いので、仮想ポイントを内部で作成しています。
その仮想ポイントをみて、等間隔の距離を抽出するので、小さい値だとカーブ上から離れる場合があります。
大きければ良いですが、その分処理が重くなるので、丁度いい値を探さなくてはなりません。
おかしい挙動をしたら、大きくしてみましょう。

Shape - Shape
シェイプを指定します。
Instans Shapeを挿してお好みのオブジェクトを割り当てられます。

Shape - Align Axis
カーブに沿って整列させるシェイプの向きです。

Shape - Is Chain
オンにすると、交互に90度回転します。
鎖だとオンです。

Up Vector - Up Vector
アップベクター機能のオンオフです。

Up Vector - Up Vector Axis
アップベクターの向きを3軸から選びます。

Roll - Roll Angle
ロールの角度を入力します。
カーブに沿ってぐるぐる回ります。

Twist - Twist Angle
ねじれです。

Twist - Profile
ねじれ具合をFcurveでコントロールします。
横軸は、カーブの始点、終点です。
縦軸は、回転の度合いです。


こういうのは、本当にICEに向いてるなと思いますが、作るのは本当に大変でした。
今、中身を見るのが正直嫌ですw
ホントは、中身の解説をしようかなーと思ったんですけど、余りに膨大すぎるので、やめました・・・。

えへ。すいません( ´థ,_‥థ`)

さて、次は何を作ろうかなー。



2011/10/27

PyQt : 常に手前に、always on top

PyQtウィンドウを、DCCソフトなどで開いて、操作している最中に、DCCソフトをフォーカスしてしまうと、背面に隠れてしまいます。

その場合、ウィンドウを「常に手前に」モードで起動して解消させます。
サンプルは、こちら。



WindowStaysOnTopHintのマニュアルにたどりつけませんでした・・・。
隠れすぎです。
Qt Designerにもそれらしきものは見当たらず。

次に、モードをトグルする場合、モードが変わったらウィンドウを再表示させないといけないみたいです。
トグルをチェックボックスで切り替えるサンプルはこちら。



alwaysOnTopStateメソッドの最後に、show()を実行して、再表示させてます。

Softimageで実行する場合、PyQtForSoftimageというプラグインを作成されている方が居ますので、そちらを使えば、Softimageにインタフェース内でQtウィンドウを使用出来ます。
これで、裏に隠れません。

プラグインダウンロード
https://github.com/caron/PyQtForSoftimage

Softimage Mailng List Archive - PyQt for Softimage Thread
http://groups.google.com/group/xsi_list/browse_frm/thread/862d4f8fb4325010

2012で試したんですが、アドオンをインストールした時点で、エラーで止まってしまうので、ちょっと書き換えました。
コードの半分チョイ下辺りです。

qtevents.py


イベント系の定数が認識しないみたいです。
とはいえ、この辺りは、余程の事が無いと使わないので、全てコメントアウトしても問題無いかな?

使うには、基本の構文ではダメで、一緒にインストールされた、pyqt_example.pyを見れば、使い方が分かります。

Steven Caronさん凄い!

2011/10/22

PyQtでラーメンタイマー

ラーメンタイマー
PyQtでラーメンタイマーを作ってみました。

ラーメンタイマーとは、お湯を入れてラーメンが出来るまでの3分を計るものです。
動画がジャスト3分の物もそう言いますが、今回はきっちり3分計るものです。


参考にさせていただいたのは、こちらです。

PyQtではじめるGUIプログラミング

というか、ほぼおんぶに抱っこ。人の褌で相撲を取るってやつです。感謝いたします。どうもありがとう!

このままやるだけだとちょっとつまらないので、PyQtにせっかく入っている初級者用と見せかけて実は上級者用らしいQt Designerを使ってやってみたいと思います(◉◞౪◟◉`)ニコッ


Qt Desinerを起動
インストールされた、PyQtから、Designerを選びます。











フォーム選択ダイアログ
立ち上がったら、フォーム選択ダイアログが出るので、templates\forms の、Main Window を選択して、作成ボタンを押します。












メニューバーとステータスバーを削除
今回は使用しないので、メニューバーとステータスバーを削除しておきます。
右クリックで、コンテキストメニューが出ますので、それぞれを削除です。









UIの配置
次に、UI部分の配置をします。

一番上に、LCD Numberを配置。

二番目に、Grid Layoutを配置してから

三番目に、Push Buttonを、Grid Layoutの中に4つ放り込みます。
放り込み方が難しいかもしれませんが、1つボタンを放り込んだあと、さらに新しいボタンをLayoutの右端に置くと、青い縦ラインが出るので、そのままドロップすると、横並びになります。
下のほうに入れ込むと、下部分に青い横ラインが出るので、その場所にドロップです。


縦に整列
全て配置し終わったら、UIを綺麗に整頓します。

MainWindowをクリックして、選択をMainWindowにします。
ボタンなどのUIが無いところで、何も選択されていない状態で右クリックして、コンテキストメニューを開きます。
ちょっとここポイントです!

右クリック → レイアウト → 垂直に並べる

を選択します。


整列しました
うまくいけば、右図のような配置になります。

失敗しても根性です。

ウィンドウの右下端をドラッグしても、レイアウトがキープされているのが分かります。







ボタン名とオブジェクト名を変更
ボタン名とオブジェクト名を変更します。
右側のオブジェクトインスペクタにあるpushButtonを選ぶと、対応して選択されるので、見ながら変更していってください。
オブジェクト名は、後々スクリプト編集するときに使うので、覚えておいてください。





シグナル/スロットの設定
次は、ボタンの挙動を作るために、シグナル/スロットを設定します。
右図の手順で、STARTボタンにシグナル/スロットの設定をします。






シグナル/スロットの設定その2
右下にある、シグナル/スロットエディタで、項目を増やしボタンとスロットを設定します。
ダブルクリックで、プルダウンが出るので、選んでください。
全てのボタンに対して行ってください。

ここまでくれば、UIは完成。

ramen_timer.ui

で保存します。

ここまでは、簡単に誰でも作れますね(☉౪ ⊙)プギャー!!!!

後は、タイマーの設定、ボタンを押したときの挙動をスクリプト編集で、実装していきます。
(Qt Designerって、スロットのコード編集って出来ないですよね?知ってる方いらっしゃいましたら教えてください。)

Shift+右クリックからコマンドウィンドウを起動
Qt Designerから作られたファイルは、uiファイルです。
これをpyファイルに変換します。
先ほど作成したuiファイルのフォルダで、Shift+右クリックからコマンドウィンドウを起動します。




コマンドプロンプトで、次のコマンドを実行して、uiからpyに変換します。

pyuic4 -i 0 -x ramen_timer.ui -o ramen_timer.py

オプションの -i は、インデントのスペースの数を表しています。0にするとインデントがタブになるので、これが嫌な方は、無くてもOKです。
SoftimageのScript Editorは、インデントがタブになってしまうので、そっちで編集する方はつけたほうがいいでしょう。

出来上がったコードは、こんな感じ。



次に、__init__メソッドを追加します。
Ui_MainWindowクラスに、countというアトリビュートを加えます。
このcountの値を減らしてカウントダウンさせます。



そのあと、QTimerオブジェクトを入れ込みます。
setupUiの一番上に追加します。
後述しますが、時間が来たらLCDNumberの値を減らすメソッドを実行させるためです。
今回は、180.00で表示させようと思うので、10msで0.01カウント減らします。
ですので、setIntervalに10を入れ、インターバルが経過したら、カウントを0.01減らすといった内容です。



次に、LCDNumberの値を初期値180.00を表示されるメソッドを書き込みます。
このメソッドは、後述します。
書く場所は、setupUiメソッドの一番下です。



準備が出来たので、シグナル/スロットのメソッドを書き加えます。
全部で5つあります。



update_display
LCDNumberにカウント表示させます。
countの値に100を割って、その値をアップデートです。

do_countdown
countの値を1減らして、update_displayします。
countの値が0以下なったら、stop_countdownを呼びます。

start_countdown
countが0より大きかったら、QTimerをスタートさせます。

stop_countdown
QTimerをストップします。
これによって、カウントが減らなくなります。

reset_countdown
QTimerをストップすると共に、countの値を18000にします。
あとで、100割るためです。
その後、update_displayで、表示を元に戻します。

ここまで来ればほぼ完璧です。pyファイルを単体でつつけば、ラーメンタイマーの完成!
しかし、Softimageで動かすことを考えると、もう少し編集が必要です。
まず、1行目の

# -*- coding: utf-8 -*-

を削除します。
その後、一番下のほうにある

if __name__ == "__main__":

を削除して、そこから下のインデントを揃えます。
最後に一番下の

sys.exit(app.exec_())



app.exec_()

に変更です!

これで、全ての機能が入りました!!!!
編集したコードは、こんな感じです。



おめでとうございます!
これで、PyQtラーメンタイマーチュートリアルは終了となります。
長々とありがとうございました。

このコードをScript Editorや、Script Libraryから実行して、完璧にうまいラーメン作ってみてくださいね~(´☣౪☣)ハフー



2011/10/19

PyQtをインストール

mayaで、標準搭載されているQtをいいなーと突っ込んでいる指を口からキーボードに移してみました。

SoftimageのPythonのインストールからちょっと書いてみます。
Softimage本体に梱包されているPythonではなく、別にインストールしたPythonを使用することにします。
PyQtを、Softimageにコピーしてもいいのですが、なんとなく気持ち悪いので、ちゃんとインストールされたものを見に行きます。

Softimage2012にインストールしてあるPythonのバージョンは、2.6.4ですが今回使用したのは、2系の最新、2.7.2を使用しています。

本家からPythonをDownloadします。
http://www.python.org/download/


Softimage で Python ActiveX を実行するには、Mark Hammond の Python 用スクリプト拡張が必要です。http://www.python.org/download/windows/

上記のスクリプト拡張は、2011/10/19現在、Build216が最新です。
2.7用の64bit版を使いました。
http://sourceforge.net/projects/pywin32/files/pywin32/Build216/


PyQtをDownloadします。
http://www.riverbankcomputing.co.uk/software/pyqt/download

Downloadするのは、Source Packagesではなく、ちょっと下にあるBinary Packagesのほうです。
今回は、こちらを使用しました。
PyQt-Py2.7-x64-gpl-4.8.5-1.exe Windows 64 bit installer


Softimageのモジュールが2012から追加されたので、siutils.pyをPython2.7.2へ追加します。
以下は、サブスクリプションアドバンスドパックから引っこ抜いた例です。

C:\Program Files\Autodesk\Softimage 2012.SAP\Application\bin\siutils.py

このファイルを

C:\Python27\Lib\site-packages

へコピーします。

最後に、環境変数をセットします。
Windows7の場合
エクスプローラからコンピュータを右クリック → システムの詳細設定 → 環境変数

Path
C:\Python27
C:\Python27\Lib\site-packages\PyQt4
PYTHONPATH
C:\Python27\Lib\site-packages

これらをそれぞれセットします。

ここまで出来たら、PyQtのセットアップは完了です。
サンプルを実行して動作するかどうかテストしてみてください。



PyQtウィジェットが動作したら、テキストボックスに何か文字を入力してボタンを押すと、Script Editorに、文字が表示されます。

さて、いろいろいじってみようかな~。
みなさんもいじってみてくださいね~。


2011/09/22

スクリプトライブラリ

仕事でよく使うだろースクリプト第3段です。

と、今回は、趣向がちょっと違いまして、みなさんは、自前のスクリプトや、ダウンロードしたものなどは、どうやって使っていますか?

普通に、フォルダ分けして、Explorerからドラッグ&ドロップでしょうか。
はたまた、シェルフを作ってボタン化している人もいるでしょう。
わざわざ、プラグインにメニュー登録して、メニューを追加している人もいるかも知れません。

それを一挙に解決出来るかも。というプラグインです。

コンセプトは

「オレのスクリプトライブラリ」

です。
初めてアドオン作りました。

addonをドラッグ&ドロップします
まずは、アドオンをダウンロードしてください。

ScriptLibrary_1_1.xsiaddon

その後、ダウンロードしたファイルを、Softimageにドラッグ&ドロップします。






初期の状態のScript Libraryメニュー
すると、メニュー上部に「Script Library」メニューが追加されました。

About Script LibraryをクリックするとHelpが表示されます。








Preferenceでルートパスを変更
このままでは、まったく使い物にならないので、Preferenceを変更します。

Preferences -> Custom -> ScriptLibrary

を選択して、スクリプトが入っているルートフォルダを設定します。


フォルダ階層と同期します
するとExplorerのフォルダ階層とメニューが同期して、そこからスクリプトファイルを実行出来るようになっちゃいます。

ちなみに、ヘルプなどのhtmlや、txtファイルも実行でき、Netviewで見ることが出来ます。

今現在対応しているファイルは

vbs, js, py, pys, html, htm, txt

の7種類が実行出来ます。
それ以外の拡張子は、メニューに登録されますが、実行出来ません。

要望があったら、バージョンアップします。

About Script Libraryも覗いてみてくださいね。

ではでは、ご活用ください。

(◉◞౪◟◉`)チャオ

2011/09/19

サブディビジョングループを作成する

タイトルを逆引きにしますた。
探しにくかったですね・・・。ごめんなさい。

さて、今日のアップは、サブディビジョングループを登録するです。
これも良く使うよなー。
サブディビレベルに応じて、グループ分けは良くしますよね?
あれ、しませんか。そうですか。

オブジェクトを選択して、実行するタイプのスクリプトです。


#---------------------------------------------------
app=Application;log=app.LogMessage;sel=app.Selection
import win32com.client
from win32com.client import constants as c
from win32com.client import Dispatch as d
#---------------------------------------------------
def CreateSubDivGroup(oObj):
log("%s,%s"%(oObj.FullName,oObj.Type))
# ポリゴンじゃないならメソッドを抜ける
if(oObj.Type != c.siPolyMeshType): return
# オブジェクトのサビディビジョンレンダーレベルを取得
iLvl = oObj.Properties("Geometry Approximation").Parameters("gapproxmordrsl").Value
# オブジェクトのモデルを取得
oMdl = oObj.Model
# オブジェクトがすでにサブディビジョングループに登録されているか調べる
oGrp = oMdl.Groups("sbdv_%02d"%iLvl)
# 登録されていなかったら
if not oGrp:
# 属するモデルにグループを作成
oGrp = oMdl.AddGroup(None,"sbdv_%02d"%iLvl)
# ジオメトリアプロクシメーションプロパティを作成
oProp = oGrp.AddProperty("GeomApprox")
# グループのサブディビジョンレンダーレベルをオブジェクトの値にエクスプレッションでセットする
oProp.Parameters("gapproxmordrsl").AddExpression("%s"%iLvl)
# オブジェクトをグループにメンバ登録
oGrp.AddMember(oObj)

# 選択したものが属するモデルに、サブディビジョングループを作成し登録
map(CreateSubDivGroup, sel)



今回のポイントは、目的のタイプのオブジェクトじゃなかったら、メソッドを抜ける。です。

if(oObj.Type != c.siPolyMeshType): return

ポリメッシュタイプ以外だったら、returnによってメソッドを抜けることが出来ます。
エラー処理のひとつです。

サブディビジョングループを登録
オブジェクトを選んで実行するとこんな感じに仕上がります。
レベルは、いくつでも大丈夫です。100を超えると数字がそろわなくなるくらいですか(そんなの作ることは無いですが)

あとは、グループに新しくサビディビプロパティを作るので、Discontinuityがデフォルトに戻ってしまうというのが、注意しなくてはならない所。
ディスプレイスもそうですね。

で。
2011から気づいたバグなのですが、グループにプロパティをセットして、オブジェクトを登録したのち、グループを消すと、元のオブジェクトに付いていたプロパティが見えなくなってしまう問題があります。
実は本体は存在するようです。その証拠に、一度保存して、読み込み直すと元通りになってます。
そんなに問題ではないように思いますが、ハマる人はハマるバグです。
今日試しましたが、2012でも同じ挙動なので、直ってませんでした。

お気をつけ。

ラヴXSI (◞≼●≽◟◞౪◟◞≼●≽◟)

2011/09/18

Render Archiveメモ

ほとんど使われる事がないRender Archive自分用メモ。

Render_Archive

Export---------------------------

Export時にライトも適用されてしまう模様。
なので、ライトは消去してからExport。
ライトのみもArchive可能。

素材分けは、パスを使用するのではなく、FrameBufferを使用する。

Motion Vectorの設定は、パス設定を見に行くので、作成時に注意。
Pass Rendering -> on
Scene Motion Blur Setting -> end of frame
shutter -> 0

Rendering------------------------

Rendering Scene構築の際、Instanceを各Passごとに設定
ICETree内で、Cacheをbtyなどで一度取り、他のPassはPointPositionのみReadし、StandinをそれぞれSet Instance Geometryで、セットする。
↑はズレル事がある。原因不明。

FrameBufferで一発出しが無難。

デフォーマのウェイト値割り当てを入れ替える

仕事中に良く使ったなーというスクリプトを続々アップしようと思いますた。
取っておいてもしょうがないし。

てなわけで、第一段は、デフォーマのウェイト値割り当てを入れ替えるスクリプトです。
うんうん、良く使う。
デフォルトで、搭載されていても、おかしくないと思うんですけどね。


#---------------------------------------------------
# デフォーマのウェイト値を入れ替えるスクリプト
# Envelopeされているオブジェクトを選択しスクリプトを実行
# その後、入れ替えるデフォーマを元、先の順にピックします。
#---------------------------------------------------
app=Application;log=app.LogMessage;sel=app.Selection
import win32com.client
from win32com.client import constants as c
from win32com.client.dynamic import Dispatch as d
#---------------------------------------------------
# チェンジ元のオブジェクトを取得
oBtn,oMdf,oElmSrc = app.PickElement("","Pick Source","Pick Source")
if not oElmSrc:raise
# チェンジ先のオブジェクトを取得
oBtn,oMdf,oElmTgt = app.PickElement("","Pick Target","Pick Target")
if not oElmTgt:raise
if sel.Count==0:raise

for oObj in sel:
    # 新しくデフォーマをEnvelopeへ登録
    app.ApplyFlexEnv(oObj.FullName+";"+oElmTgt.FullName, "", 0)
    # Envelopeを取得
    oEnv = oObj.Envelopes(0)
    # EnvelopeのWeight値を取得
    # 内包表記を使用して、タプルをリストへ変換
    lWgt = [list(t) for t in oEnv.Weights.Array]
    # チェンジ元、先、それぞれのデフォーマは
    # 何番目に登録されたオフジェクトかを調べる
    for i,oDfm in enumerate(oEnv.Deformers):
        if oElmSrc.FullName == oDfm.FullName:
            iSrc = i
            log(i)
        elif oElmTgt.FullName == oDfm.FullName:
            iTgt = i
            log(i)
    # デフォーマのウェイト値を入れ替える
    lWgt[iSrc],lWgt[iTgt] = lWgt[iTgt],lWgt[iSrc]
    # ウェイト値をセットする
    oEnv.Weights.Array = lWgt



ここでのスクリプトのポイントは、ウェイト値の取得です。
Pythonのタプルは編集が出来ないので、編集が出来るリストへ変換します。

lWgt = [list(t) for t in oEnv.Weights.Array]

2次元配列のタプルを、2次元配列のリストへ変換します。
これで、編集可能ってわけです。

多次元タプルを、全てリスト化するメソッドを今度書いてみようかな。

2011/08/24

ウェイトが100でないポイントを選択する

調べたいオブジェクトを選択して、実行です。


l=list()
for i,w in enumerate(Application.Selection(0).Envelopes(0).Weights):
    if round(sum(w),5)!=100:
        l.append(i)

Application.SelectGeometryComponents(Application.Selection(0).FullName+".pnt["+",".join(map(str,l))+"]")


内包表記を使うと1行で出来たよ。


Application.SelectGeometryComponents(Application.Selection(0).FullName+".pnt["+",".join([ str(i) for i,w in enumerate(Application.Selection(0).Envelopes(0).Weights) if round(sum(w),5)!=100])+"]")

Perforce: 複数のワークスペースを更新するバッチ

batを叩けば全部更新。 @echo off set P4PORT=x.x.x.x:xxxx set P4USER=user set P4PASSWD=password echo %P4PORT% echo %P4USER% echo %P4PASSWD% echo %P4PAS...