2008/12/10

Sukio Sukio Sukio Python - VI

さてさて、6回目です。
大分経ちましたが、いかがお過ごしでしょうか?
最近、サボっている気がしますね・・・。
忙しいと思ってくださいね。
更新がんばります!
とはいいつつ、今年最後の更新な気がしますね・・・。

そういえば、読んでますメールを初めて戴きました。
ウレシイデスー。
見てくれる人がいらして感謝ですね。
(某Pさんありがとうございます)
これからも精進いたしますね。

では本題に入りましょう。
やっとか、という感じですが、とうとうPythonの使い方に入ります。
いままでは、XSIの使い方しかご紹介してませんでしたので序の口です。
でも、ワタクシもPythonは、初歩的な使い方にとどまっていますが
Stringでの簡略の指定法、Listの使い方、強力なモジュール群、などなど紹介したいと思います。

Pythonでは、Stringの指定も楽々に行えます。
全てを解説すると、1回で終わらなくなってしまうので、良く使うやつをざっくり抜き出して使ってみますね。

例えば、人骨には左右が存在します。
右手の骨、'IK_Rhand'というオブジェクトを選択して
対応する左手の骨'IK_Lhand'を取得したいときどうすれば良いでしょうか?
そして、ほかの名前にも対応する書き方はどうすれば良いでしょう?

回答は、LとRが対応しているものを探せば良いのです。

Rを選択していて、実行したらそれに対応するLを選択するというものを書いてみます。

こんな感じでしょうか。

app = Application
sSel = app.Selection.GetAsText()
app.Selection.SetAsText(sSel.replace('R','L'))

これだけで、選択が全てL側に移ると思います。
少々脱線してしまいますが、良く使うSelectionをちょっとだけ解説しますね。

Selectionオブジェクトは、アプリケーションのグローバルセレクションです。』

とマニュアルには、書いてありますが、なんのこっちゃ良く分かりませんが、Selectionは、XSI特有のオブジェクトです。
mayaやMotionBuilderなどの主要ソフトは、オブジェクトごとにSelectフラグというものが用意されているのですが、XSIは、Selectionという変数に属しているものが選択されるというちょっと一風変わった仕様なのです。
ようするに、Selectionにオブジェクトをぶち込めばSelect状態になります。逆に空にすれば全て選択が外れます。
変な感じですが、意外と使いやすいです。
この説明でも良く分からないかもしれませんが、String解説に戻ります。

sSel = app.Selection.GetAsText()

これで、String型としてsSelに入れます。
どういう形で返るかというと

null
null1
null2

と選択していたら

"null,null1,null2"

といった具合に返ってきます。
LogMessageは、Stringしか受け付けませんから、エラーチェックのときは、GetAsText()を多用します。

app.Selection.SetAsText(sSel.replace('R','L'))

SetAsTextは、SelectionをStringに書かれている通りに選択します。
そして文字列メソッドのreplaceを使いRからLに変えています。
replaceは、置換です。

'IK_Rhand,IK_Rarm'

でしたら

'IK_Lhand,IK_Larm'

に置き換えます。

けれども、これだけでは、Lを選択してもRに行きません。寂しいです。
ですので

Lを選択のときは、Rを選択する。
Rを選択のときは、Lを選択する。

というちょっとした仕込を入れましょう。

import string
app = Application
sSel = app.Selection.GetAsText().encode()
table = string.maketrans('LR','RL')
sSel = sSel.translate(table)
app.Selection.SetAsText(sSel)

翻訳ツール(translate)を使って、LとRを逆に認識させるというコードです。
ステキなモジュール、stringを呼びました。
これは、文字列とは違います。stringというモジュールです。
モジュールを呼ぶときには、importとして、classdefを外部ファイルから呼ぶときに使います。
おまじないみたいなもんです。
そして通常翻訳するときには、'hello'は'こんにちは'に変換するみたいな感じでやってますが、同じ事を文字列で実行します。

まず、変換テーブルというものを作ります。

table = string.maketrans('LR','RL')

これは

'L'は'R'にしなさい
'R'は'L'にしなさい

という翻訳機が使う変換テーブルを作成します。
中身を見てみますと・・・

'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'


・・・ワケ分かりません。
ギャボンヌです。
Application.ES_Speak('gabonnu')

まあ、コンピュータが理解出来ればいいので
こんな感じですよ。って教えてあげます。

そして、変換テーブルに沿って文字列を翻訳します。

sSel = sSel.translate(table)

'IK_Larm'という文字列なら'IK_Rarm'に翻訳します。
そうそう。translateは、str型でないと機能しないようです。
XSIは、str型でも結構unicode型で返してくるファンクションが多いので、少々注意です。

encode()で、str型に変換しています。

sSel = app.Selection.GetAsText().encode()

そして最後に

app.Selection.SetAsText(sSel)

として、Selectionをセットしなおします。
これで、反転セレクトツールが出来ましたね。

『やったよ!ママン!ボクにも出来た!』

しかし、喜ぶのはまだ早いです。
ここでもまだワナが潜んでおります。
キャラクターなどは、Model階層化で作られることがほとんどです。
キャラクター'ROB'というModelに属していたらどうでしょうか?

・・・上手く取得出来ません。

'ROB.IK_Rarm'を翻訳すると、'LOB.IK_Larm'になってしまいます・・・。
目標が見つからなくエラーが出てしまいます。

ああ、これじゃクソスクリプトじゃんか。
これは、どうしよう・・・。
悩む・・・。
悩むなぁ・・・。

なんて感じでスクリプト制作はまだまだ続きます。
というわけで、この続きは次回までのお楽しみです。
完成目指して、がんばってみますね。
ではでは、しばらくの沈黙をいただきます。

冬休み早くこねーかなー。

2008/11/28

XSI 7.01 Released.

やー。出ましたね。
少しは、プロダクションで使えるようになったでしょうか。

Fixed Bugs Listを見てみると恐ろしい量です・・・。

UDEV00248322 - Incidence Node "Light List" is broken
IncidenceのLight Listは直ったみたい?

UDEV00248548 - Loading the Vector to Color node from the top menu does not work
Vector to Colorが見えるようになったのかな?

UDEV00247262 - Hidden Line Display and material with Transparency can cause XSI to crash
うひひっ。恐ろしや。

でも、7.02が出るまで我慢だなぁ。

2008/11/26

Sukio Sukio Sukio Python - V

みなさまこんにちは。5回目になりました。

さて、今回は、OM、オブジェクトモデルの探し方です。
せっかくオブジェクト指向の言語Pythonを使用していますので、XSIも合わせて使ってみましょう。

その前に、オブジェクト指向とはなんでしょうか?
ワタクシも上手く伝え切れませんが、XSIをモデルに考えると、Explorerをイメージすると分かりやすいと思います。
Nullには、Primitive、Kinematics、Visibility、Display
とプロパティの引き出しがあり、そのそれぞれにパラメータを持ち、値を変えることが出来ます。
Pythonの中にもこういった階層構造があり、stringでさえオブジェクトモデルでありその下には

'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find',

'index','isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join',

'ljust','lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',

'rsplit','rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate',

'upper','zfill'


と本当にたくさんの便利なメソッドが用意されています。
これらを使いこなしてこそ、Pythonを使う意味があるというもの。
OMを使わなければ、Pythonでなくても良いのです。
このように、XSIのメソッドを知るということと、Pythonのメソッドを知るということは別のことであり、両方学ばなければ十分に力が発揮されないことが理解出来ることでしょう。

では、本題に戻りましょう。

まず、テキトーに物を作って、階層構造を作って、ランダマイズを掛け、値をいじって、移動して、レンダリングをオフ、グループを作成して、というCMのログを見てみます。


Application.NewScene("", "")
Application.GetPrim("Null", "", "", "")
Application.CreatePrim("Sphere", "MeshSurface", "", "")
Application.CopyPaste("sphere", "", "null", 1)
Application.ApplyOp("Randomize", "sphere", 3, "siPersistentOperation", "", 0)
Application.SetValue("sphere.polymsh.randomizeop.amplx", 2, "")
Application.SetValue("sphere.polymsh.randomizeop.amply", 2, "")
Application.SetValue("sphere.polymsh.randomizeop.amplz", 2, "")
Application.Translate("", 1, 0, 0, "siAbsolute", "siPivot", "siObj", "siX", "", "", "", "", "","", "", "", "", 0, "")
Application.Translate("", 0, 1, 0, "siAbsolute", "siPivot", "siObj", "siY", "", "", "", "", "","", "", "", "", 0, "")
Application.SetValue("sphere.visibility.rendvis", 0, "")
Application.CreateGroup("", "", "")


こんな感じです。
多くのスクリプトは

Application.SetValue(oObj+".polymsh.randomizeop.amplx", 2, "")

などして、汎用性を高めているに留まっています。
これを実行しても、立派にスクリプトと言えるでしょう。
ですが、ちょっと見にくい感じです。
ですので、もう一段階レベルを上げる事を考えて見ます。
このCMをほとんどOMに変えてみます。


app=Application
app.NewScene()
oRoot = app.ActiveSceneRoot
oNull = oRoot.AddNull()
oObj = oNull.AddGeometry('Sphere','MeshSurface')
oOp = XSIFactory.CreateObjectFromPreset( "Randomize", "Operators" )
oOp.connect(oObj)
oOp.amplx.Value = 2
oOp.amply.Value = 2
oOp.amplz.Value = 2
oObj.posx.Value = 1
oObj.posy.Value = 1
oObj.rendvis.Value = False
oRoot.AddGroup(oObj)


物凄くスッキリしました。
そして、実行すると一瞬で返ってきます。
CMは、NewScene()のみです。

app=Application

まず、Applicationはとても長いので、appと短い変数にしてあげます。
xsiとしている人も居ます。自分が分かればOKです。

さて、Scene_Rootは、何を実行すれば取得出来るでしょうか?
ApplicationのSDKマニュアルを見てみましょう。
このオブジェクトには、これだけのメソッド、プロパティが用意されています。

メソッド
ActivateWorkgroup  AddCommand  AddWorkgroup  Advise  
ClassName  CreateAddon  CreateCommand  CreateProject  
ExecuteScript  ExecuteScriptCode  ExecuteScriptCommand  GetCommandByScriptingName  
InstallAddon  IsClassOf  IsEqualTo  LoadPlugin  
LogMessage  RemoveCommand  RemoveWorkgroup  RescanWorkgroups  
Unadvise  UnInstallAddon  UnloadPlugin  UpdatePlugins  
Version           

プロパティ
ActiveProject  ActiveSceneRoot  ActiveToolName  Application  
Categories  Commands  Desktop  Devices  
Dictionary  EventInfos  Filters  FullName  
Help  InstallationPath  Interactive  License  
Name  NestedObjects  Origin  OriginPath  
Parent  Platform  Plugins  Preferences  
Selection  StatusBar  Type  Workgroups  


たくさんありますね。

Application.Selection

としてあげれば、選択されたものが取得出来ます。
肝心のScene_Rootですが、プロパティの2つ目、ActiveSceneRootがそれに当たります。

oRoot = app.ActiveSceneRoot

Scene_Rootに行う処理は、2回ありますので、取り合えず変数oRootに入れておきます。
次に、ActiveSceneRootにNullを追加したいので、ActiveSceneRootのページに行き戻り値Modelというリンクを辿ってみましょう。
これまた物凄くたくさんのメソッド群ですね。
ここからNullが出そうなものはAddNullというメソッドを利用しようと思います。

oNull = oRoot.AddNull()

またまたリンクを辿るとAddNullには、ひとつ引数を与えられるみたいです。
Stringを与えれば、その名前で追加します。

AddNull('null_generate_by_script')

などですね。今回は、省略します。
ついでに出したNullは、oNullという変数に入れてあげます。

oObj = oNull.AddGeometry('Sphere','MeshSurface')

nullの子供にSphereを加えたいので、AddGeometry()を使います。第一引数は、プリセット。第二引数は、メッシュなのかナーブスかです。これもついでにoObjとしておきます。

oOp = XSIFactory.CreateObjectFromPreset( "Randomize", "Operators" )
oOp.connect(oObj)

オペレータは、ApplyOp()を主に使いますが、今回は、完全OMにしてみました。
Randomizeオペレータを一度シーンに呼び出し、適用させたいオブジェクト(この場合oObjです)にコネクトします。
ですが、ApplyOp()はとても便利ですので、そのまま使ったほうが良いときがほとんどです。
この場合は

app.ApplyOp("Randomize",oObj)

でOKでしょう。
ただし、返り値が問題です。
コマンド、ApplyOp()は、配列のXSICollectionで返ってきてしまいます。
オペレータオブジェクトの外側にXSICollectionという殻が付いている状態です。
(OperatorObject)←こんな感じ。

ですので、殻の中身を食べたいのでこの様に大抵(0)を付けてあげて、0番目の配列を選んであげます。
これにより、オペレータオブジェクトを直に変数に入れています。

oOp = app.ApplyOp("Randomize",oObj)(0)

こんな感じですね。oOpには、ランダマイズオペレータが入っています。
それでは、ランダマイズオペレータのDisplacementをXYZ全て2にします。
Operatorで検索をかけ、Operatorページに行ってみましょう。
おや?アンプリチュードの値を変えられそうなメソッド、プロパティは、無さそうです。
そうなのです。パラメータの値は、いちいちマニュアルには書いて居ないのです。
パラメータの名前を知るには、オペレータを選択して、Inspectし、Markingします。
Xをクリックしてみましょう。
するとログには、こう出ます。

Application.SetMarking("amplx")

DisplacementのXの値は、"amplx"という名前なのが分かりました。
ですので、この値をセットするには

oOp.amplx.Value = 2
oOp.amply.Value = 2
oOp.amplz.Value = 2

このように書きます。
本来ですと

oOp.Parameters("amplx").Value = 2

と書きます。
これはショートカットがききますので、ダイレクトにも書くことが出来ます。
ショートカットで検索すれば、いろいろ出てきますので、参考にしましょう。
出来ないものと出来るものがあるので、注意です。
出来なかったら、Parametersを使えばOKです。

oObj.posx.Value = 1
oObj.posy.Value = 1

Kinematicsも同様の探し方です。
ショートカットを使いましょう。
本来の書き方は

oObj.Kinematics.Local.posx.Value = 1

です。
さて、次は、VisibilityのRenderViewのチェックをオフです。
これもショートカットが存在します。
良く使うものには大抵ついています。

oObj.rendvis.Value = False

これでOKです。

oObj.Properties("Visibility").Parameters("rendvis").Value

どちらでもといった感じでしょうか。
ちなみに、Explorerで、四角いノードは、プロパティを示しています。
名前が分かれば、Propertiesで取得出来ます。

最後に、グループです。
グループは、モデルに付くものです。ですので、Modelのマニュアルを覗いて見ると
ありました、AddGroup()。
これを使用しましょう。

oRoot.AddGroup(oObj)

第一引数は、メンバーを入れます。
ここに入るのは、OMでないと受け付けてくれません。
OMで処理するには、OMを使用しなければなりません。

Application.CreateGroup(sName,oObj)

逆に、CMは、StringでもOMでも入れればGroupに入れてくれるようです。
融通はこちらのほうが上でしょうか。

現存のグループの名前が分かっている場合の取得方法は

Application.ActiveSceneRoot.Groups("ABC")

とすれば、ABCという名前のグループが取得出来ます。
こちらも便利でしょ?

という感じです。
未だPythonのOMには、たどり着きませんでしたが、いかがでしたか?
今回は、Pythonの部分に全く触れていないので、この説明は、vbでもjsでも同じことが言えます。

題材をちょっと間違えたかもなぁ・・・ω
ま、いっか。

OMには、いろんなメリットがあり、必要なオブジェクトに瞬時にアクセスすることが出来ます。
そして、処理スピードには、天と地の差が開く事になります。
覚えるのは結構大変ですが、SDKマニュアルとお友達になれば、いつでも助けてくれることでしょう。

というわけで、次回は、Pythonの使い方に注目してみましょう。
String、List辺りをやってみようと思います。
ファイル操作もやってみてもいいかも知れません。

それでは、またお会い出来るのを心待ちにしてます。

おたのしみに。

2008/11/10

Softimage : Sukio Sukio Sukio Python - IV : UIの基本構造

今回で、4回目になりました。
スクリプティングは、毎回同じことをやらせるのにとても都合のいいものです。
寝ている間に、作業を終わらせることだって可能なのです。
物量が無ければ、ほとんど意味の無いものですけど。

さて、今回は、カスタムプロパティを使い、なんらかの動作をさせるボタンを作成してみましょう。

単純に、ボタンを押したら選択されているもののposXをパラメータの値に移動するというものを作ってみます。


from win32com.client import constants as c

LogicCode = """
def PosX_OnClicked():
    app = Application
    log = app.LogMessage
    fNmb = PPG.Number.Value
    app.Selection(0).Posx.Value = fNmb
    log(fNmb)
"""

app = Application
log = app.LogMessage

oPSet = app.ActiveSceneRoot.AddProperty(
    "CustomProperty",
    False,
    "ButtonTEST" )

oPSet.AddParameter2(
    "number",
    c.siDouble,
    0.0,
    -20.0, 20.0,
    -20.0, 20.0,
    c.siClassifUnknown,
    c.siPersistable | c.siAnimatable )

oLayout = oPSet.PPGLayout

oLayout.AddButton( "PosX" )
oLayout.AddItem( "Number" )

oLayout.Language = "Python"
oLayout.Logic = LogicCode

app.InspectObj( oPSet, "", "", c.siLock )


いささか難しいコードになってしまいますが、一番シンプルな形ではないでしょうか。
CMは、InspectObjのみとなってしまいますが、解説をいたします。

XSIでのUIのダイアログの事をPPG(Property Page)と呼びます。
PPGを作るには、3つの段階が必要になります。

1. 内部的に処理するパラメータを定義する。(AddParameter)
2. PPG内のレイアウトにどのような形で変数を表示させるか。(AddItem)
3. ボタン、チェックボックス、ドロップダウンボックス、などをユーザーが押したり値をいじったら何を実行するか。(Logic)

まず、必要なパラメータを準備しましょう。
AddPropertyで、カスタムプロパティを作成します。

oPSet = app.ActiveSceneRoot.AddProperty( "CustomProperty", False, "ButtonTEST" )

ActiveSceneRootは、XSIの"Scene_Root"モデルを意味します。
そこにプロパティ"ButtonTEST"を付けなさいという命令文です。
イコールで繋ぐと、oPSetには、カスタムプロパティが入ります。

次に、選択したもののposXを入れたいのですから、浮動小数点のタイプを用いたほうがいいでしょう。
AddParameter2で、変数を作成します。

oPSet.AddParameter2("number",c.siDouble,0.0,-20.0, 20.0,-20.0,20.0,c.siClassifUnknown,c.siPersistable | c.siAnimatable )

この変数名をnumberということにします。
第2引数は、タイプですから、c.siDoubleという浮動小数点の定数を入れます。
あとは、表示するバーのデフォルトの値、最大最小値、内部的な制限の最大最小値、パラメータの分類(多くはデフォルトでOKです)
c.siPersistableは、値を保存可能に、c.siAnimatableはこのパラメータ自体をアニメーション可能にします。
物凄くたくさんのオプションが存在するので、マニュアルを見て、いろいろ試してみましょう。

次に、レイアウトを決めます。
oLayout = oPSet.PPGLayout

カスタムプロパティには、PPGLayoutというメソッドが存在します。
コレを用いて、レイアウトにアクセスします。
レイアウトに表示させたいものは、Numberというパラメータバーと実行するボタンです。

oLayout.AddButton( "PosX" )
oLayout.AddItem( "Number" )

ボタンを作成するときは、AddParameter2を用いなくても大丈夫。
ボタンにパラメータは必要ありません。
ボタンの下に、numberというパラメーターバーを表示させます。

ここまで、実行して、出来たプロパティをつつけば、レイアウト通り出来ているはずです。
ただし、ボタンを押しても何も動きません。
動作させるには、Logicメソッドを用いて、PPGに仕組みを覚えさせなければいけません。
この形で仕組みを書くには、LogicコードをStringで渡してあげなくてはならないのです。

Stringを使う際、"(ダブルクォーテーション)または、'(シングルクォーテーション)でくくった形になります。
しかし、Pythonでは、インデントや改行などがあり、通常一行で書くにはとても辛い所です。
ですが、'''を使う事によって、書いた文面を見た目通りに変数に入れることが出来ます。


LogicCode = """
def PosX_OnClicked():
    app = Application
    log = app.LogMessage
    fNmb = PPG.Number.Value
    app.Selection(0).Posx.Value = fNmb
    log(fNmb)
"""


LogicCode変数は、'''から'''までというニュアンスです。この中の

fNmb = PPG.Number.Value

という部分。これは、PPGのNumberというパラメータの値を参照します。
fNmbという変数に入れています。(自分だけ分かればいいので、float Numberの略です)
Selection(0)は、一番初めに選択したものになります。
ちなみに、Selection(1)は、二番目に選択したものが入ります。

app.Selection(0).Posx.Value = fNmb

ここで、ポジションXの値をfNmbから受け取っています。
フルで書くと

Application.Selection(0).Kinematics.Local.Posx.Value = fNmb

となりますが、キネマティクスのローカルは、ショートカットがききます。

最後のCM

app.InspectObj( oPSet, '', '', c.siLock )

これは、カスタムプロパティを左クリックでつついたように表示するってコマンドです。
c.siLockは、ページをロックします。
他のオブジェクトをInspectしても表示を切り替えないようにするためです。

以上、PPGの作り方の解説でした。
少々分からないことだらけでしょうが、一個一個解析していけば、素晴らしいUIが完成すること間違いなしです。

ちなみに、この書き方の弱点は、保存して、次に開いたときにレイアウトが保存出来ないってところです。
今回の、ボタンとパラメータバーだけでは大丈夫ですが、コンボボックスやら、チェックボックス、FCurveエディター、などなど、豪華に作ったはいいが、消えてしまうのね~というのが開発者として、一回は通る道になっております。
それを回避するには、カスタムプロパティ自体をプラグインとして登録してあげるしかありません。
これは、マニュアルや他の人が作ったやり方を参照したほうが、覚えるのは早いでしょう。
幸い、CustomPropertyWizardという恐ろしく便利なプリセット作成ツールがあるので、それを使ってくださいね。

次回は、OMを使って、パラメータにアクセスする方法の見つけ方でもやりましょうか。

ではでは、ゴキゲンヨウ!

2008/10/25

Sukio Sukio Sukio Python - III

Python好きよ好きよ好きよな初心者のためのPython講座その3です。

今回で、恐らく脱落者続出だと思われますが、意味が全然分からなくても

「ああ、オレってだめぽ・・・。スクリプト向いてないぽ・・・。」

って、思わないで欲しいです。
一朝一夕で覚えられたら、この記事を読まなくてもPythonを使いこなせるはずです。
Pythonは、いつまでも貴方を待ち続けます。

さて、始めましょう。

今回は、def文です。
defは、他の言語の言う所の、functionに当たります。
同じスクリプトで、何度も同じ内容のものを繰り返すときや、処理を見やすくす
るとき、そして、Pythonでは、前に保存したスクリプトのdefを呼び出すことも、簡単に行えます。

まず、何度も使うといえば

Application.LogMessage()

この文は、何度もデバッグのためなどに多用することでしょう。
ですので、例えばログ文を少し豪華にしたいなどのときや、何回も使うものをまとめておきます。

def log(pMsg):
  Application.LogMessage(pMsg)
  return '+ + + ' + pMsg + ' + + +'

log('abc')
log(r'C:\Softimage\XSI_7.0\Application\bin\XSI.exe')
sReturn = log('I love XSI')
log(sReturn)

このようにして、LogMessageを簡略化出来ます。

Pythonの場合、defを変数にすることが出来、最良手はコレですが。
log = Application.LogMessage
log('abc')

logというファンクションを作り、受け取る変数pMsgをセットします。これは、
何個でも作成することが出来ます。

def log(pMsg,pType):

こんな感じです。

下の、logファンクションで、'abc'というストリングを第1引数に入れ実行する
と、上部のpMsgという変数に、'abc'が代入され、

Application.LogMessage('abc')が実行されるシンプルなファンクションです。

def文の中に、return文がありますが、コレは返ってくる値をセットすることが出来ます。
無くても動作します。
'I love XSI'がpMsgに代入されたとすると、返ってくるのは、'+ + + I love XSI + + +'になり、上記の例では、sReturnに代入されるという仕組みです。

r'C:\Softimage\XSI_7.0\Application\bin\XSI.exe'

この文で、スクリプトを書いたことがある方は、へぇ~と思いますが、Pythonには、Row Stringという型が用意されており、バックスラッシュ'\'を通常の文字として認識させることが可能です。
例えば、JScriptで書くには、このようにしなければ、'\'は認識できません。

'C:\\Softimage\\XSI_7.0\\Application\\bin\\XSI.exe'

バックスラッシュの嵐が訪れる事になります。

String型で'\'は、エスケープ文字と呼ばれ、改行やタブを表現することが出来ます。
これにはいろいろあります。

\\:バックスラッシュ (\)
\':一重引用符 (')
\":二重引用符 (")
\a:ASCII 端末ベル (BEL)
\b:ASCII バックスペース (BS)
\f:ASCII フォームフィード (FF)
\n:ASCII 行送り、改行 (LF)
\r:ASCII 復帰 (CR)
\t:ASCII 水平タブ (TAB)
\v:ASCII 水平タブ (VT)
\uxxxx:16-bit の 16 進数値 xxxx を持つ文字 (Unicode のみ)
\Uxxxxxxxx:32-bit の 16 進数値 xxxxxxxx を持つ文字 (Unicode のみ)
\ooo:8 進数値 ooo を持つ文字
\xhh:16 進数値 hh を持つ文字
\N{name}:Unicode データベース中で名前 name を持つ文字 (Unicode のみ)

いろいろありますが、何のことかサッパリ。って感じでしょうか。
ここで、使うだろうなっていうのは、バックスラッシュ(\)、ダブルクォーテーション(")、改行、タブくらいでしょうか。
それだけ覚えておけば、豪華なLogMessageや、ログとしてテキスト吐き出しをするときなど、応用が可能です。

さてそれでは、defを絡めつつ、PickSessionを使用して、動作するScriptを書いて見ましょう。

1. Pickする度に、原点に移動する。
2. 終了するには、右クリックする。

という内容にしましょう。

まず、たくさんのNullを出してバラバラに配置しておきます。
Iで作ったScriptを実行してもOKです。
もちろん、自分で作成されるのがベストです。


def MatchTransformationPickElement():
  oBtn = 1
  while(oBtn != 0):
    oBtn,oMdf,oElm = app.PickElement()
    if oBtn==0: log('Done'); break
    app.ResetTransform(oElm)
    log(oElm)

app = Application
log = app.LogMessage
MatchTransformationPickElement()


こんな感じでしょうか。内容を見ていきましょう。
プログラム的に言えば、上から順に実行されて行きますが、defに関しては

一度メモリに溜め込んで、他の行で実行されて初めて効力を発揮する

というニュアンスです。
このコードは

app = Applicationが一番最初に実行されます。
次に、log = app.LogMessage
次に、作ったファンクションである

MatchTransformationPickElement()

が実行されます。
oBtn = 1 や whileなどがありますが、まず、注目していただきたいのは、PickElement()です。
実は、PickElement()もファンクションなのです。
PickElement()を実行すると、Picking Modeになります。
そして、ユーザーがマウスボタンを押すと3つの戻り値、Return Valueを得ます。

ButtonPressed, ModifierPressed, PickedElement

ButtonPressedは、なんのボタンを押したか。
1:左クリック
2:中クリック
0:右クリック
このような数字が返ってきます。

ModifierPressedは、装飾キーは押しているか。
0:なし
1:+Shiftクリック
2:+Ctrlクリック
3:+Shift+Ctrlクリック
今回は、使いません。

最後のPickedElementは、XSIのオブジェクトを返します。
詳しくはPickElementをハイライトして、マニュアルを見てみましょう。

oBtn, oMdf, oElm = app.PickElement()

変数を並べておけば、それぞれに代入されます。
左クリックでNullを選んだとすると、oBtnに1が、oMdfには0、oElmには、Nullが入る事になります。
次の文に進みます。

if oBtn==0: log('Done'); break
app.ResetTransform(oElm)

前回やった、if文ですが、このようにセミコロン(;)で改行を代用しこのように書くことが出来ます。
つまり、oBtnが0のとき、これは、右クリックしたときに、

log('Done')
break

を実行することを意味します。
終了したいので'Done'とログし、break文で、whileを抜けます。
この場合、defは、もう何もないので、ココで終わります。
breakを実行すると、forwhiledefの途中で終了出来るのです。

左クリックをした場合、ifは、スルーされ

app.ResetTransform(oElm)

が実行され、Nullが原点に戻ります。
実は、ここでスクリプトは、終わりません。それはwhile文というものがあるからです。
while文は、条件式がTrueであれば、ずーっといつまでも実行し続けるというものです。
if文のループ版みたいな感じでしょうか。かなり強力です。
しかし、while文を多用するのはあまりオススメしません。
一歩間違うと、いつまでたっても終わらないスクリプトが完成してしまいます。
ちょっと恐ろしい文です。

while(oBtn != 0):

は、oBtnが、0じゃないとき。つまり、右クリックじゃないときにずーっと実行しなさいという意味になります。
def文のはじめに

oBtn = 1

を定義しているのは、whileに一番最初に入ったとき、oBtnという変数は存在しませんので、エラーになるのを回避するためです。

さて、こんな感じですが、お分かりになりましたでしょうか?
defは結構つかみにくいものであったと記憶してますので、無理に使わなくてもスクリプトを書くことは出来ます。
ですが、構造が理解できると、必ず使うようになります。
少しずつ覚えて行くと良いでしょう。

今回も物凄く長くなってしまいました・・・。
スクリプトは、細かい定義がたくさんあって、説明するのが大変です。
もうちょっとシンプルに行きたいものです。

次回は、GUIの作成をしてみたいと思います。
カスタムパラメータを作り、ボタンをプチっとしたら、何かが起こるというものです。
これが出来れば、結構面白い事が出来るでしょう。
楽しみの幅が広がりますね。

ではでは、またの機会に・・・。

2008/10/18

Sukio Sukio Sukio Python - II

Python好きよ好きよ好きよな初心者のためのPython講座その2です。

今回は、if文をご紹介。
ついでに、for文のもうひとつの使い方をご紹介します。
今回は、Selectionをからめた使い方です。
選択したものに対してのみ、実行するスクリプトを作ってみましょう。

前回のIで作ったNullを作るスクリプトで、10個の並んだNullを作りましょう。

スクリプトを使って、ある特定のオブジェクトを取得するというのは
常に苦労するところです。
コーディングをしていて、1/3くらいはそこに注力します。
ですので、最重要であるといっても過言ではありません。
気を遣って充分考えて取得しましょう。

選択したものを変数に入れるには次の命令文を書きます。

oSel = Application.GetValue("SelectionList")

または

oSel = Application.Selection

です。
上記はCMを使い、下記はOMを使用しています。
変数に代入されるのは、内部的には決定的に違いますが、結果はどちらも大して差はありません。
10個のNullを選択して、実行してログを見てみましょう。
ログを見るには、この命令文です。

Application.LogMessage(oSel)

VBやJなら、両方とも選択したオブジェクト名がずらーっと表示されますが、Pythonの場合、ちょっとだけ違う結果になってしまいます。
ここで変数のデータの型という定義が存在するのですが、進めながら少しずつお話しましょう。

さて、選択された10個のNullを取得することが出来るようになりました。
次に、その10個に対して、Sizeを少しずつ大きくしていく事にします。

NullのSizeを変えるコマンドを知りたいので、ためしにひとつ選んでSizeを変えてみます。

Application.SetValue("null.null.size", 2, "")

このようなログが出てきました。
SetValueの第1引数は、ターゲット。第2引数はその値です。

先ほど選択したものの変数oSelと前回のforを使い順々に処理していきます。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  app.SetValue(oObj.Name+".null.size", i, "")
  i += 1


ここでのポイントは、forに対し、rangeではなくoSelという変数を入れています。
oSelのデータ型は、XSICollectionです。
[null,null1,null2,null3,null4,null5,null6,null7,null8,null9]
内部的には、このような格納されています。
変わってrange(10)のデータ型は、リストです。
[0,1,2,3,4,5,6,7,8,9]
内部的には、こんな感じ。
Pythonでの配列(Array)は、リスト、タプル、ディクショナリーと種類が存在しますがXSICollectionは、リストにとても近いです。
ですので、oSelの左から順にoObjという変数に格納して、forのインデント内を実行しなさい。
となります。
1順目は、null(OM)がoObjに格納されて、処理されるという仕組みです。

次に、SetValueの第1引数ですが、ターゲットを順に変えていく必要があります。

app.SetValue(oObj.Name+".null.size", i, "")

oObjには"null"が入っています。oObjは、オブジェクトモデルなので、Nameというパラメーターを取得出来ます。
そして、iには、1が入っています。

なので内部的には、こう解釈されます。

app.SetValue("null.null.size", 1, "")

最後の

i += 1

で、iに1が足されますので、結果2になりました。
これで、1順目が終了です。

次に2順目に行きます。
仕組みは分かっていますので、次の命令文が実行される事になります。

app.SetValue("null1.null.size", 2, "")

こんな感じで、次々に処理され、NullのSizeがどんどん大きくなるというスクリプトが出来ました。

それでは、次のステップに進みましょう。

並べられた、Nullですが、全てに処理を適用するのではなく、ひとつ飛ばしに実行するには、どうしたらいいでしょう?
ここで、if文が登場です。
iの値が、偶数のときのみNullのSizeを変えるというのを実行することにしてみます。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  if i % 2 == 0:
    app.SetValue(oObj.Name+".null.size", i, "")
  else:
    app.SetValue(oObj.Name+".null.size", 1, "")
  i += 1


if文は、後に条件式が来ます。演算子(ここでは "==" お互いに等価であることを意味します)を境に、True(真)なのかFalse(偽)なのかを判断するだけです。
ここでの条件式は

i % 2 == 0

%は、剰余を表します。剰余とは、割り算をしたときの余りのことです。
つまり、2で割ったときに余りが0になるのは偶数なので、割り切れるときにTrueとなり、if文の中身が実行される仕組みです。

else文は、ifFalseのときに、実行されます。
iが奇数のときは、Sizeを1にという命令文が実行されます。

それでは、その上、4番目のNullにだけは、Sizeを0.1にするということをするには、どうしたらいいでしょうか?

4番目のNullだけに適用したい場合、iの値が4では、偶数なので

i % 2 == 0

この式に、さらに、4以外という条件を付与しなければなりません。
その値以外という演算子は!=を使います。

i != 4

ですので、偶数だけど、4以外という条件式は

if i % 2 == 0 and i != 4:

とやり、andを入れます。

そして4になるときの処理は、if文がFalseになり、elseにたどり着く前にif文を定義しなくてはなりません。
そのときは、elif文です。else ifを省略した形ですね。

elif i == 4:

この文を使って、こんな感じになりました。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  if i % 2 == 0 and i != 4:
    app.SetValue(oObj.Name+".null.size", i, "")
  elif i == 4:
    app.SetValue(oObj.Name+".null.size", 0.1, "")
  else:
    app.SetValue(oObj.Name+".null.size", 1, "")
  i += 1



4のときだけ、elif文が適用されるようになりました。
これで、4番目のNullにだけ、Size 0.1が適用されます。

if文は、こんな感じで、とてもシンプルです。
複雑になるのは、条件式の部分。ここには、結構毎回悩まされますが、ロジックを探すのもスクリプトの面白さと言えるでしょう。

今回も、かなり長くなってしまいましたが、コレでほぼPythonは手中に入れたようなものです。
ほとんどのスクリプトは、for文とif文で成り立っています。
物を自動的に判別するというところが、時間が一番かかるところ。
処理するものは、Nullなのか、PolygonMeshなのか、Lightなのか、Passなのか。
XSIには、とても多くのオブジェクトタイプが存在しますが、それを上回る判別方法がたくさん存在します。
それをひとつひとつ見つけて行くことが進歩の秘訣です。

それでは今回はこの辺で。
次は、defでしょうか。これも大切なものでファンクションと呼ばれるものです。
オブジェクトをPickして、実行するタイプのものを作りましょう。

では、次回まで・・・。

オタノシミニ☆

2008/10/14

Sukio Sukio Sukio Python - I

スクリプトの羅列とかいって、全然書いてなかったので、ちくちく書いてみる事にします。

ですが、VBは良く分からんですし、いまさらJScriptをやるのはなんなので
Pythonにスポットを当ててみようと思います。

なんで、いちいち標準で入っている、VBやJを使わないん?

って思いますが、Pythonを触り始めると、元に戻るのがイヤんなります。

だまされたと思って、門をくぐってみるとたちまち便利な事に気づくでしょう。
ただ、これを感じるのは、他の言語をやった人のみですが・・・。

しかしながら、初心者の方もPythonからハジメテミルのはとっても良いと思います。

なので、早速ゆーっくり初めてみましょう。

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

まずはじめに

初心者の方には全く分からないと思いますが、XSIの命令文とPythonの命令文があります。
XSIの命令文というのは、

GetPrim()

とか

SetValue()

とかXSIのマニュアルに書いてある命令文たちです。

変わって、Pythonの命令文は

for

とか

if

とかが存在します。
こちらは、XSIマニュアルには書いてません。
webなどを参考にするしか手はありません。

最初は、全然分からないと思いますが、見分けられるようになると、次のステップに上がったのかなと思ってもらってOKです。
ちなみに、Pythonの命令文を覚えておきさえすれば、他のソフトでも同じやり方で通用するということになります。

このサイトでは、Pythonの命令文XSIの命令文と、このように表記しますね。

それでは、第一回目。
当然、forを取り上げたいと思います。

for i in range(10):
  Application.LogMessage( i )

2行のこのスクリプトでは、0~9を表示せよ。
という内容です。

コピペして、スクリプトエディターで実行してログを見てみましょう。
数字の羅列が表示されたと思います。

それでは、2行目に注目してください。
Pythonでは、インデントというものが、存在します。
インデントというものは、forの下の行のスペース、段落を意味します。
これは、スペースでも、tabでもOKです。

for文は、インデント内を繰り返す命令文です。
他の言語では、インデントを付けるのは習慣で、無くても動作するんですが、Pythonでは、無くては動作しません。
他の制作者が作っても必ずこの形になるので、コードが見やすくなるという特徴もあります。

range文は、数字の羅列を生成します。
結果を見ても分かりますが、0~9を生成します。
10は、生成しません。
コンピューターは、0が一番最初の数字と認識します。
そして、10未満までというニュアンスでOKです。

in文もありますが、簡単に言うと

0~9を i という変数に順々に代入して、インデント内の処理をしなさい。

という意味になります。

ほとんどのスクリプトは、このような、ループ処理を多用しています。

では、次に、nullを10個出してみましょう。

app = Application
for i in range(10):
  app.GetPrim("Null")

for文の処理の中身だけを取り替えました。
そして、行が増えましたが、少し簡略化してみました。
Applicationと書くのは、少々手間ですので、appという変数にしてみました。
このように、"."(ドット)の前は他の変数にする事が出来ます。

そして、書き方には、もう一通りの書き方があります。

app = Application
for i in range(10):
  app.ActiveSceneRoot.AddNull()

このようなものです。
上記は、コマンドと呼びます(以下CM)
XSIは、ボタンを押すとコマンドを呼び出して、結果を表示します。
下記は、オブジェクトモデルです(以下OM)
メニューやボタンからでは、呼び出すことが出来ない直接的なやり方です。
計算を内部的に行い、スクリプトが終わったと同時に結果のみを表示します。
スピードは、こちらのほうが、断然早いです。
100個作成する。といった、スクリプトで試してみると一目瞭然だと思います。
ただし、いきなりOMから入ると本当に訳が分からなくなるので、最初からはあまりオススメしません。

次に進みます。
ただ出すだけではつまらないので、作る度に1グリッド移動して配置するというものを作ってみましょう。

一度、nullを作成して、xに1移動します。

Application.GetPrim("Null", "", "", "")
Application.Translate("", 1, 0, 0, "siAbsolute", "siPivot", "siObj", "siX", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

これをコピペして、また同じように、for文の中身だけ取り替えてみましょう。
ですが、ここで、ちょっとしたポイントがあります。

app = Application
for i in range(10):
  app.GetPrim("Null", "", "", "")
  app.Translate("", i, 0, 0, "siAbsolute", "siPivot", "siObj", "siX", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

1のところをiにちょこっとだけ、変えています。
スクリプトエディターで、Translateというところをハイライトして、?ボタンを押してみてください。
マニュアルが表示されました。
カッコ"()"の中身にいろいろ書いてありますが、iの部分の第2引数("ひきすう"といいます)は、X座標を指定してあげればいいことが分かります。
ですので、rangeで生成したiの変数を使用して、X座標を入れてあげます。

nullを作ってXの0~9の座標に移動しなさい。

といった、スクリプトが出来ました。

XではなくYに移動させたい場合は第3引数のYのところをiにすればいいので

app = Application
for i in range(10):
  app.GetPrim("Null", "", "", "")
  app.Translate("", 0, i, 0, "siAbsolute", "siPivot", "siObj", "siY", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

このように書けばOKです。
第8引数のsiXが実は曲者ですが、フィルターをsiYにしましょう。

そして、ちょっとダイエットするとコレだけで動作します。

app = Application
for i in range(10):
  app.GetPrim("Null")
  app.Translate("", i)

実は、マニュアルの[]でくくられた引数は、省略することが出来ます。
省略したらどうなるのかというと、デフォルト値がセットされることになっています。

書き方はいろいろありますが、簡単な方法でやります。
一番スマートな書き方は、このようになります。

app = Application
oRoot = Application.ActiveSceneRoot
for i in range(10):
  oNull = oRoot.AddNull()
  oNull.posx.Value = i

OMを使用し、作ったNullを変数にして、posxパラメータをダイレクトに入力しています。
何度も書きますが、スピードは、歴然です。

for文の中にfor文を書くことも可能です。

app = Application
oRoot = Application.ActiveSceneRoot
for ix in range(10):
  for iy in range(10):
    oNull = oRoot.AddNull()
    oNull.posx.Value = ix
    oNull.posy.Value = iy

100個のNullが碁盤の目に並びましたね。

と、このあたりで、訳が分からなくなる人続出でしょうから、for文は、このくらいにします。
次回は、ifですね。

オタノシミニ!

2008/10/07

画角と解像度の緻密な関係

今回はメモ的なログで。

元の画像をキープしたまま、画像の見える範囲を広げたい。
と思ったことはありませんか?

ズームアウトで外側の枠だけを広げて2倍の解像度にしたい場合
カメラの画角をどうやって設定したらいいか分かりますか?



『画角を2倍、解像度2倍でいいんじゃね?』

最初は誰もがそう考えるはずです。
ワタクシも疑いませんでした。
だけど、実際は違うのです。

角度を2倍にしたら、見える範囲は2倍になるのは正しいのですが・・・。
なんでかは、あんまり証明できないけれど、実際は違うみたいです。

Mayaには、カメラスケールというものが存在していて、簡単に結果を出すことが出来たりしますが
残念ながら、XSIには、標準でそんなものは用意されていません。

なので、ピッタリ来る計算式を出してExpressionを組んで見ましょう。

中学校でおなじみの懐かしの三角定理を使います。

下図は、トップビューからの図です。



緑の角度 x は、三角定理を使うので元の画角の1/2を示しています。
求めたい赤の角度は x' として、これまた1/2とします。

aは、仮想のプレーンを想像して、そこまでの距離の長さ
bは、解像度の1/2の長さと仮定します。

最終目的はbがn倍になったときに、x' がどのように変化するかです。

まずタンジェントを使います。

tan(x)=b/a ...①

が成り立ちます。

xを出すには、arctanを使います。
アークタンジェントとは、タンジェントの逆関数で、角度とか座標を求めるときに良く使ったりします。かなり便利なので、覚えて置くと◎です。

tan(x) = y のときに、arctan(y) = xになります。

これを利用して

arctan(tan(x))=x

①の式を使って

arctan(b/a)=x

です。
bをn倍したら x' になるので

arctan(nb/a)=x'

になります。なので、また式を戻してみます。

arctan(n・tan(x))=x'

になります。これで、角度の半分が求まります。

結果が求まりました!
XSIのExpressionは、こんな感じでしょうか。

atan(n*tan(Camera.camera.fov/2))*2

ちなみにですが、ソフトによって、tan()は、radianしか入らなかったりするんですが
XSIは、degreeで入ってくれますね。
pythonでコレをやる場合は、radianをdegreeに変換して使いましょう。

import math as m
x=40
n=1.1
m.degrees(m.atan(n*m.tan(m.radians(x/2))))*2

こんな感じですね。
話がそれましたが・・・。

数学って、こんな感じで使うんですよねー。
美しいものです。

こうやって、数学を覚えたりしたら、もうちょっと勉強したんだけどなぁ。
数学って楽しいものなんですよね。

2008/10/02

XSI7 - Solve a freezing probrem

XSI7、入れた当初は、不安定すぎ・・・。
これじゃ、全然使えなさ過ぎ・・・。
win2000互換じゃないとsample sceneが、開けなかったり、viewerが、shadedモードで
サブディビかけただけで、青画面になったり・・・。

などなど、不安だらけでしたが、青画面のログを見てみると、display driverなんちゃらかんちゃら、って書いてあったので、思い切って最新のドライバーに更新してみました。

したら、全部解消されました♪

win2000互換じゃなくても、sample落ちません♪

サブディビも落ちなくなりました♪

そのほかは知りませんが、ちょっとは前進したかも。

みなさんも、最新のドライバー試して見るといいかもです。

またエラーがあったら、報告しますね。

2008/09/10

Real Time Ambient Occlusion via ICE

うひゃー。とても疲れました。

でも、ワタクシでも出来ました。
リアルタイムアンビエントオクルーヂョン。



めちゃくちゃ苦労しました・・・。
しかも、出来たものは激重・・・。
このシーン2000ポリしか無いんですが、CPUメーターがすぐ70%とか行ってしまいます。
しかも、落ちやすいし・・・。
さらに、VertexColorにコネクト出来ず、WeightMap止まりです。

アルゴリズムの簡略化というものは、奥が深すぎます。
ICE、奥が深すぎます。

しかしながら、AOのアルゴリズムは、比較的簡単です。

あるポイントから、ランダムに複数のレイを飛ばし、その遮蔽率でそのポイントの明るさを決定するものです。

一行で終わるのか・・・orz

なんだけど、CGの世界では、ベクトルが、物に当たるということを表現するのに物凄い労力が必要なのです。
衝突判定の勉強をしていて良かったと思いました。
とはいえ、最適化までは、手が伸びず、挫折しましたけど・・・。

このICEノードで、苦労したところはー

ポイントに対して、どうやって、ランダムレイをジェネレイトするのか。
んー、まったく思いつきませんでした。
それを解決したのは、コレ。

Random Integer
http://community.softimage.com/downloads.php?do=file&id=138

Moduloで、ランダム値を発生させるとは・・・。アメイズィング!ジーニアス!
素晴らしすぎるので、5ツ星を付けてみました。
こういう単純なものって、宝物ですわね。
Halfdan Ingvarsson様に感謝です。

あとは、helge mathee様のTutorial - Arrays in ICE on Vimeoです。


Tutorial - Arrays in ICE from helge mathee on Vimeo.

ArrayのValueをリニアに作ることすら出来なかったワタクシには、とてつもなく参考になりました。
感謝です。ありがとうございます。

XSI7のtrialが、今日切れるので、しばらくお預けですな。
でも、出来てほっとしましたYO!

次は、何作ろうかなー。

2008/09/01

ICE - Deformation Basic

ICEを使った、とても基本的なDeformationです。

現状のバージョンでは、基本的にICEは、Kinematicsに値を入れることは出来なくなっています。
環境変数を変えれば可能ですが、サポート対象外です。なんでかはナゾですが。

ですので、ポイント情報を、ヌルの変換にそのまま追従させる、poseコンストもどきをやってみました。



ICE Treeの構造です。

cubeに、ICE Treeを構築します。
Set Dataに、self.PointPositionをセットします。
Multiply Vector by Matricsノードを繋ぎます。
Get Dataノードに、self.PointPositionをセットし、上記ノードのvectorに接続します。
別のGet Dataノードに、null.kine.localをセットし、Matrixに接続します。



以上で、nullの動きを伝えるICE Treeが完成です。

次に、WeightMapを利用して、2つのnullをコントローラーとした
Envelopeの基本のICE Treeです。



Set Dataにself.PointPositionをセットします。
Multiply Vector by Matrixに接続します。
Get Dataノードに、self.PointPositionをセットし、上記ノードVectorに接続。
Linear Interpolateノードを取得し、MVbMのMatrixに接続します。
上記ノードのFirstにGet Dataノード、null.kine.localを接続します。
SecondにGet Dataノード、null1.kine.localを接続します。
Blendに、Get Weight Map Valueをつなげれば、完成です。



多分、これをたーくさん増やしていけば、ICE Envelopeが完成するはずです。
でも、面倒なので、やってません・・・。
ハッキリ言って、Compounds待ちです。そこまで、頭良く無いのです。残念ながら・・・。
Dual Quaternionが待ち遠しいなぁ。

早く登場しないかなー。

2008/08/14

Scalingのワナ

Scalingには、トラップが仕掛けてあります。

何にも知らないで、ほかのソフトでアニメーションを付けXSIで、再現しようとして、Importすると

んじゃコレっ?!

てな結果があったりします。
そのほか、nullの軸が90度になっておらず、変な形になっている?

・・・まいっか。


という経験がある方もいらっしゃると思います。

XSIのScalingには、2つのモードがあるのはご存知でしょうか?
Kinematics Local のScalingタブの、Scaling Computationという欄に

Hierarchical (Softimage) Scaling

とうものが存在します。
通常は、オンになってます。

下図は、

上オフ
下オン

になっていて、左のnullが親で、右のnullを子供にしていてposX:3、rotY45がそれぞれ入っています。



親のnull(左)をぞれぞれ、Xに0.5スケールしてみます。
するとこんな感じです。



スケールの印象からすると、オフ状態の上が正しい結果なんでしょうね。
見た目に、nullが親の形状に沿って変形しています。
反面、Defaultの、Hierarchical (Softimage) Scalingをオンにした状態のnull(下)では、子供のsclxのみにしか変換がかかりません。

これは、Rotationをかえて、どんどんと階層を作った場合、Scaleの変換が正しく行われないことを意味します。

ということで、オフにして、作業すればOKです。

気をつけなければならないことは、MotionBuilderなどで、作ったFBXをImportして、骨に流し込むときには、Hierarchical (Softimage) Scalingは、オンにしていないとうまくきません。
MotionBuilderのScalingのモードは、Hierarchical (Softimage) Scalingがオンのときと一緒です。
なので、Importするときは、オンにして、読み込みましょう。

バチコン来ます!

2008/08/12

XSI 7 and Python Install Tips



遂に、我がマシンにも導入されました。
いじり倒してやるっ。

と思ったのも束の間、ICEの敷居の高さに、正直ビビリマシタ・・・。

はっきり言って、デザイナーが気軽に覚えられるものでは到底ありません・・・。
スクリプトをちょっと書けるレベルだけじゃ全然ダメです。
CGソフトのことを心底愛していないと使いこなすことは出来ません。

た・だ、そこを克服することが出来れば、レベルはまた一段階上がることでしょう。
ICE MASTERの道は、かなり果てしないものに見えますが、一歩一歩上がって行けば、大丈夫です。(きっと)



そんでもって、やっとわが社にもPythonが導入される兆しが見えました。
やっりぃ。
XSIで、Pythonを使うには、ActivePythonをサーバーに入れるってのが
一番楽でしたが、ActivePythonは、あくまで擬似らしくPythonとは違うみたいです。

なので、Pythonじゃなきゃいやじゃ!

とゴネて、無理クリやってもらいました。

そもそも、XSIで、Pythonを使えるようにするには、

http://www.python.org
本家から、Python本体をDownloadする。

http://sourceforge.net/projects/pywin32
から、さらに、Python for Windows ExtensionsをDownloadして
Installしなくてはなりません。

このExtensionsが曲者でして、Pythonが、ローカルマシンにInstallされていないと
Installerが機能しない点にありました。
全てのデザイナーが使える環境にするには、一台一台入れていかないといけない状態です。

ですが、この手順で、サーバーにPythonをおいて、それを見に行けるようすることが可能です。

1. 一旦ローカルにInstallしてしまい、XSIを一度起動して、すぐ落とします。

2. %PythonPath%\Lib\site-packages内の拡張モジュール類を、サーバーPythonのsite-packagesフォルダ内にコピー。

3. ローカルにある、PythonのDLL類も全てサーバーにコピー。
(python25.dll、pythoncom25.dll、pywintypes25.dll←コレたちだけかな?エラーが出たらスミマセンそれもコピーで)

4. 環境変数のPython Pathをサーバーに指定。ついでにPathもPythonフォルダに通しておきます。

5. %PythonPath%\Lib\site-packages\win32comext\axscript\client\pyscript.pyを実行。
(レジストリを書き換えるスクリプトなので、Admin権限が必要です。)

で、出来ました♪

一部、気をつけなければいけないことは
%PythonPath%\Lib\site-packages\win32com\gen_py
ここの中身は、ユーザーが書き込めるようにしなければならないので

%PythonPath%\Lib\site-packages\win32com\__init__.py
内のgen_pathを書き換え

__gen_path__ = os.getenv('TEMP')

などにして、Localに作るようにします。

コレで、たくさんのマシンにインストールしまくることなく、サーバーをアップデートなりで
一括で管理出来る事になります。

ううーん。スバラシイ!

というわけで、JScriptとは、オサラバ。サヨウナラ。
Mayaも、MotionBuilderも、Pythonなので、Python一筋です。

がっつり行きましょうー。

2008/08/07

XSIのトップをひたすら走る方々

同じXSIとして、心強いですねー。

psyop - HP Maestro

psyopの作るものはいつ見てもホレボレします。
めちゃくちゃカッコいいス。



The Mill - Behind the Scenes of Mitsubishi Eclipse Robot Factory

The Millは、ワタクシ外部に対してとても弱いので、この前のセミナーで初めて知りました。
うひっ。1000万ポリゴンすカ。レベルの違いを感じます・・・。

2008/08/06

Manage Device Drivers



んん~?またしても、全然知らなかった機能が・・・。

Animation -> Tools
-> Devices -> Manage Device Drivers

今日、初めてボタンを押しました・・・orz
一体何年使ってるんだ・・・。

コレってなんだか、SIにもあったような気がする・・・。
全然覚えて無いけど。



使い方は、Addから、Mouseを選択。
MouseプロパティをInspectして、そのままActiveをOn。

Mouseの動きがキャプれます。
コレは使えるカモ!!!
(って思った人は、何人居るんだろう・・・。)

Defaultでは、Mouse-xが、選択されたもののposxにバインドされていますな。rotにも出来るのが嬉しいですな。

中クリックで、キャプチャー開始。右クリで、キャプチャー終了。

左クリックは、なんも入っていないので、Command - FirstFrameとか入れておけば、スタートフレームに戻れますね。

早速、カメラの画ブレモーションに入れてみよう。上手くいくかなー?

2008/08/05

mental ray の基本中~の基本。

アンチエイリアシングの設定をどうすればいいのか分からない?
そんなの簡単だよ。こうやってこうやってこうすりゃいいじゃん。
なるほどね。こういうふうに設定すればいいのか。

でも、設定した値の意味は、分からないまま。

現状では、分かっている人が、分かっていない人に仕様書を渡せば良かったのです。
それでほとんどエラーなく、すんなりレンダリングが出来たはずでした。

だけど、近年、データの巨大化が進む中、同じ設定では、レンダリングが出来る出来ないが顕著になってきました。

Hair、FG、SSSなどなどなど・・・。管理職泣かせの技術たち。

そこで、エラーで止まってしまうフレームが出てきた場合どのようにしてレンダリングをかけられるようになるのかを結構はしょりで説明したいと思います。

今回は、エイリアシングの設定から。

Aliasing ( Raytracing & Scanline Only )とSampling Contrastには、常に密接な関係があります。

まず、Sampling Contrastから説明することにしましょう。
この設定の値はカラーです。RGBAの4つの値から成っています。
この値は、レンダリングされる絵のピクセルとピクセルの差を見ています。

■□
↑ピクセルのつもり

例えば、上記のようにピクセルが、RGBAが0(つまり真っ黒)と1(真っ白)の様な絵があったとしましょう。2ピクセルの絵と考えてください。
その場合、ピクセルとピクセルの色の差は、1-0の1になるわけです。
現在のSampling Contrastの設定は、全て0.2ですので、Aliasing ( Raytracing & Scanline Only )のMax Levelでアンチエイリアシングがかかります。
この場合、Max Levelの1が適用されます。

■■

次の例は、真っ黒なピクセルが隣り合った場合です。
この場合は、ピクセルとピクセルの色の差は、0-0の0です。
Sampling Contrastと比較して差が許容範囲なので、Aliasing ( Raytracing & Scanline Only )のMin Levelでアンチエイリアシングがかかります。
この場合は、Min Levelの-1が適用される訳です。(ほんとは、もうちょっと複雑な判定をします)

画像でコントラストの差を視覚的に見てみましょう。



ま、だいたい、0.05だと人間視覚的に限界なんじゃね?ということが分かります。
ということは、最終レンダレベルだと、Sampling Contrastは0.05くらいで良さそうですね。
プレビューなら、0.2とかにして、Min Levelの面積を大きくしてあげれば、プレビュー時間を短くすることが出来ます。

これで、色の差のコントラストが少ないときにはレベルを下げて、色の差が大きい場合はレベルを上げてレンダリング時間を効率よく使う事が出来ます。

例えば、真っ黒な部分の計算をしようとしたとき、全てのLevelが2の高い状態で計算するのは時間の無駄だという事です。
なぜ、時間の無駄かといのは、次の説明を読めば分かることでしょう。

Aliasing ( Raytracing & Scanline Only )のレベルとは、どういう意味なのか。
0の場合は、そのピクセル単体をそのまま計算します。



アンチエイリアシング無しの状態です。ギザギザ。

1の場合は、ピクセルを4分割して、サブピクセルとして計算します。



ちょっとまだ、アンチエイリアシングが甘いかな?という状態です。
でも、ブラーがかかるのなら、このくらいで十分いけるし、0でもいいんじゃね?って感じです。

2の場合は、ピクセルを16分割して、サブピクセルとして計算します。



これくらいなら、ほぼジャギる事は無いでしょう。
これ以上上げると時間が重くなり過ぎて、自分の首を絞めることウケアイ。

つまり、Levelが1の場合は、単純に解像度を2倍で計算している事とだいたい同じになります。
Levelが2の場合は解像度4倍です。

逆に、-1の場合はどうなるかというと、4ピクセルに対して、1回しか計算しようとしません。
つまり、Levelが-1の場合は、なんと解像度1/2で計算している事と同じになるのです。
こんな設定仕事では、使ってはいけません(結構居るんだな。コレが。※ちなみに、Vrayでも大体同じ)

ようするに、最終レンダリングをする場合、-1という値を入れて計算をするというのは、得策ではありません。
当然、ジャギーが発生する事になります。
ただし、Sampling Contrastの設定次第では、うまくいくかもしれません。
0.005の様な、肉眼で分からないくらいの色の差であれば、-1で計算しても良いかもしれません。
けれど、4ピクセルに対して1回しか計算できないので、細い剣の様な物をレンダリングする場合や、テクスチャがとても細かい場合、レイがヒットすることなく真っ黒なピクセルとして計算されることが多々あります。

逆に、Levelが2であって、16ピクセル分の計算をしたとしても、それ以上のテクスチャの解像度がある場合、ちらつきが発生してしまいます。
意味無さ杉くん。
テクスチャの解像度がデカければデカいほど良い訳では無いことを十分理解しておいてください。
キャラクターがどのくらいのサイズでレンダリングされるのかを判断して、テクスチャのサイズを小さくする事が必要になります。
テクスチャのサイズを小さくすることで、メモリの確保も出来るほか、ちらつきを抑える事が出来るでしょう。

ちなみに、メモリに関して、マニュアルにはこんな事が書いてありました。

メモリ要件の計算で使用される標準規則では、サーフェイス上の三角形1000個につき1MBのメモリが割り当てられます。
512× 512のテクスチャごとに、1MBの追加メモリが必要になります

だそうです。
つ・ま・り、1Triangleで、1B消費するってことだから、100万Triangleで、1GBのメモリを消費するってことです。

分かりやすっ。

(でも、結構長い間、知らずに過ごして来ました・・・。)

でもこれ、ギガポリゴンコアって、どうなるんだろう・・・。
ギガポリゴンコアだから、大丈夫なのか?!
IIになるし、大丈夫なのか?!

最後に、アンチを決定する際、熟練の技として

まず、これで、いいだろうと思われる値をセットします。
次に、3枚だけレンダリングします。
3枚をループ再生します。

コレだけで、ちらついているかが分かるようになります。

だいたい。

だいたいね。

だいたいだかんね。

だから、全フレ計算するなんて、アホなことすんなよっ。

2008/08/04

ぴくショー克服

Syflexなど、Simlationしたオブジェクトで、Collisionに挟まれると、ポリゴンが暴れてしまう、四面楚歌ぴくぴく現象(略してぴくショー)が起きるかと思います。
ぴくショーを直すのは至難のワザですし、時間がかかりまくります。
モーションキャプチャーのアニメーションも、ノイズと呼ばれる、ガコッとなるキーが入る場合も多々ありますが、その場合は、その部分のキーをがっつり消去するなどで、簡単に直すことが出来ます。
ですが、Defomationなのでポイントのアニメーションは、通常見ることが出来ません。
ましてや、相手はcache。ファイルの中身をエディットする気にもさらさらなりません。

そこで、ポイントのアニメーションが編集出来ればいいなーと思うので
ポイント相手に、ひとつひとつnullをplotして、アニメーション編集をしてみることにしましょう。

問題点を挙げます。

  • まず、1つは、Defomerを生かした状態で、デザイナーが編集しやすいように、その上になんらかのConstruction Stackを付け、外部にRigを作る必要があります。
  • 次に、そのRigは、視覚的にPointと同期して欲しいことでしょう。
  • 最後に、大量のPointに対して処理を行うには、Scriptを書いたほうがいいでしょう。

この3つをクリアするだけで、OKです。



ポイントClusterを作りnullにアニメをObject to Clusterで、くっ付けます。
そのnullをplot。このnullは見れなくていいので、IconをNoneにします。コンストも抹殺しましょう。

そのnullの子供にnull1を作り、SRTを0に設定。このnullがコントローラーになります。
IconをCircleなどにしておきます。

さらに、null2を作り、null1にTranslationのXYZにそれぞれExpression = で繋ぎます。このトランス情報が、Deformerの差分として、加えられればいいので今度は、逆に、null2を先ほどPlotしたCluster PointにCluster CenterでDeformを加えます。
null2も見えなくていいので、IconをNoneにします。

これで、Rigプロセスは、完了しました。

あとは、コントローラーのnull1をGlobal Trans Plotして、ギザギザしているアニメーションをなだらかにするだけです。
Plotしないで、オフセッターとしても使い道はあります。

あとは、履歴などをたどって、Scriptにするだけです。

このプロセスの弱点は、Clusterが死ぬほど出来ることと、nullが物凄くたくさん出来ることと、元のオブジェクトにSRTのアニメーションが入っている場合は、うまくいきません。
あと、研究が足りないのでcageしたものに対してこのプロセスをすると失敗します。

ICEで、もしかしたらもちっと楽に出来るのかなー?と思いつつ淡い期待を抱く今日この頃。
の前に、もっと簡単なフローがあるかもなー?と思ったりもして。

というわけで、また新たなワザ見つけたら、書いてみます。

2008/08/01

Nullちゃんで、ライティングしてみた。

Scene内に、ひとつもライティングが無い状態で、Nullのみでライティング。
やってみました。



Scene内には、オブジェクトは、SphereとNullとカメラのみです。
後は、Nullの位置を見るShaderを作ればOK。

というわけで、繋いで見ます。



普段は見慣れないノードがたくさん刺さってますね。
ワタクシも仕事では、一度も使った試しがありません。

やることは、サーフェイスの向きとライティングの向きの内積を出せば拡散反射になるということです。

まずは、二股に分かれている上の部分。

XSI_Model_Mapノードを使用して、オブジェクトのNormal情報をゲット。
XSI_Model_Mapノードは、0から1の範囲で、サーフェイスの表面の向きをRGB値として取得します。
上を向いている面なら、( 0.5, 1, 0.5 )を返します。
左を向いている面なら、( 0, 0.5, 0.5 )を返します。
このままだと、ベクトルの向きにはならないので、Vector_math_vectorノードを使って2倍して1引きます。
すると、範囲は-1から1の正しい向きの値が取得出来ます。
上なら、( 0, 1, 0 )
左なら、( -1, 0, 0 )です。

次に、下の部分。

Color2vectorノードのColor InputにNullのポジション情報をExpressionで繋ぎます。
このままだと、長さが1ではないベクトルが返ってくるので、Vector_math_vectorノードでNormalizeして1の長さにします。
これで、ライティングの向き情報が取得出来ました。

二つの情報が出揃ったので、Vector_math_scalarノードを使い、Dot Product(内積)を取得します。

このスカラー値こそが、世に名を轟かせているかの有名なLambertです。

特にこんなこと知らなくても、仕事出来ますし、意味ねーじゃんと思いますが
CGソフト作るってものすげー大変なんだなーって思いました。

いやいや、開発者には頭が上がりません。

人体ミクロの世界のトテモキレイな demo reel

キモくないと思いますが、臓器のとってもキレイなデモムービー。

Hybrid Medical Animation
http://www.hybridmedicalanimation.com/demoReel.html

BGMも、トッテモカワイイですね。
ちなみに、MOGWAIの他の曲のクリップはこちら。
Friend Of The Night
http://jp.youtube.com/watch?v=K_NrZCZwlhc

うーん、たまらんっ。たまんねーなっ。
CGじゃなくてもいいものはいいねぇ。

2008/07/31

Timeline で Animation 編集

アニメーションが入っているノードを選択すると、Timeline にキーが赤く表示されます。
これ移動できねーかなーと思ってたら出来ました。

Timeline上で、Shift押しながら左ドラッグで範囲指定が出来るって知ってました?








いやー、知りませんでしたよね?そうですよね?意外でしたよね?

さらに、中ドラッグで、キーが移動出来ます。
囲まれた範囲の左っ側を中ドラッグするとスケールも出来るんですよね。
右っ側も同じ。
キーを削除するには、右クリでメニューだして、Deleteを選ぶと消えます。

DopeSheet が無くともこれで十分じゃないのか?!

Timeline エライ!

Animation Layer のあれこれ

Animation Layerは、ウソクリップです。略してウソップ。
Mixerに表示されるくせに、移動もカットも出来ません。
出来るのは、Weightが変えられるくらいです。
Clipの上にLayerを付けてアニメーションを修正出来るすっばらしいものなんですが
一つのClipとして、Mergeするには、ちょっとしたワザが必要になります。

これには、2通りあります。

1. まず、普通にSourceClipをApply Action(もし、Clipを移動やスケールをしている場合、Freeze and Replaceなどで新しく作り直します)
して、LayerをMergeして、Fcurveにしてから、Storeする。

2. もしくは、Layerを加えたノードに対し、Global Plotを実行してから、ClipをFreeze and Replaceして、LayerとGlobalアニメを削除。

ちょっと大変です。ウソップ。

Layerの弱点は他にもいろいろあってー

たとえば、Clipを移動したいときには、Layerにも同じ変換を加えなければなりません。
スゲェ大変。

あと、致命的だなと思うのは、LayerをMergeして集約したとしても、元のアニメーションとは完全には一致しません。
出来るアニメはちょいズレです。
ホントにちょこっとだけど。

Plotするときに、Fitをかけてしまうんですね~。
これには、オプションを付けてほしかった。

ま、いいのかな。ほとんど分からないので。

カメラのアニメーションなど、キーの位置はここがいいのに!
って場合は、絶対オススメしません。
キーが増えるだけだし。

で、これならどうだ?!って思った方法があるのだけど。

結構意外だったのが、LayerをActionとして保存して、読み込むと普通のClipになっていること。
だけど、普通にMixerに並べても、Clip同士ではアニメーションが平均化してしまうので
Mixer Properties...のNormalizeをオフにします。
このプロパティは、Mixerに付くので、全部のClipが加算になります。
なので、必要なClipをCompaundして、CompaundのMixer Propertiesから、Normalizeをオフにして
CompaundしたClipのみを加算にします。
で、Freeze and ReplaceすればClip完成。

のはずだと思ったのも束の間。今度は、Rotationがズレてしまいました・・・。
Rotationは、やはり難しい・・・。

出来たら報告する予定。

2008/07/30

Transformの優先順位

コンストやらMixerやら、同じノードに複数のアニメーションを入れることが出来ますがー
最強なのはどれでしょう?

と、気になったアナタは偉いです。

ちなみに、こんな感じ。

1. Constraint
2. Global Transform Fcurve
3. Animation Mixer
4. Local Transform Fcurve, Expression, Scripted Operator

ということになってるみたいです。

ということで、ここでちょい技。
コンストした状態、もしくは、グローバルを Marked Parameter Plot した状態では
Mixerは、無効になります。
ですがこのときに、ClipをFreeze and Replaceすると
Sourceに登録されているParameterのみがPlotされます。

つ・ま・り、Clipがコンストorグローバルのアニメーションになるってことです。

Apply Actionしなくて済みます。

ちょっと素敵でしょ。

Schematic View の Nキー

最近知りました。ががーん。

Schematic Viewで、ノード選択して、Nキーを押すと
階層がしまわれたり、広がったりの Expand/Collapse が出来ます。
マウス中ダブクリでも出来るけど、このほうがTree選択されないから楽チン。

さらに、ノード選択して、Alt押しながら矢印キーで、階層を上下左右に移動出来ます。
結構便利。

さらにさらに、Alt押しながらノードをドラッグアンドドロップすると
そのノードが子供になります。
これも、Treeが選択状態にならないので、便利。

さらにさらにさらに、ノード選択して、Ctl+Shift+Tで、モデル階層が選択出来るって知ってた?

結構いろいろ便利機能あるよねー。

Pose Constraint と Position & Orientation Constraint

Pose Constraint

Position かつ Orientation Constraint の2重コンストは
結果が違いマス。

同じになりそうだけどネ~。

感覚としては、Poseは、Parent/Childの関係とほぼ一緒の挙動をするのに対し
Position、及び、Orientationは、数値のみをリンクするって感じ。

ちなみに、Rotation値の違うもの同士を Orientation の CnsCompで、
コンストした場合、大抵うまくアニメーションを写せません。
なぜかっていうのは、Euler(オイラー)の解説をしなくてはならないのですが
理解していません。
難しすぎるんだもの・・・。

うまく行かないなーと思ったら、Poseコンスト。

これで、大丈夫。

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...