myara CG blog

CG Design Blog. Thoughts, experiments and experiences.

SI | TMPフォルダとトークンについて(Scripting)

久しぶりの記事ですね。最近忙しくてあまりブログ書く暇がないし、面白いネタもないです。
Mayaシリーズの続きを書こうと思っていたけど、何を書こうとしたっけ?・・・。

久々の記事だけど、ただのスクリプトメモになってしまいます。

スクリプトを書かない人にはどうでも良い情報ですけど、
スクリプトを書く人にはちょっと便利かもしれない:


TEMPフォルダの取得

XSIUtils.Environment("TEMP")



これでWindowsのTEMPフォルダではなく、SoftimageのTEMPフォルダを取得が出来ます。
Softimageを閉じる時にこのフォルダが自動的に消えます。
スクリプト内で一時的に何かを吐き出すには便利ですよ。

マニュアルには「システム環境変数に読み取り」しか載っていません。

SDK マニュアル: XSIUtils.Environment



Token の使い方
PPGにフォルダパスを入れて、プロジェクトフォルダにPicturesフォルダとか入れると
めちゃくちゃ長いパスになってしまい、PPGには入りきれないですね。

こういう時にトークンを使うと便利です。
[Project Path]
とかですね。

D:\\myara\\Projects\\TopSecret\\The_Project\\SI\\Pictures



なんかより

[Project Path]\\Pictures



の方がスッキリしますね。


同じく、[User]、[Scene] などのトークンもあるので、ユーザー名とか取得する時にも使えますね。


例えば:

// JScript
String_Path = "[project path]" + "\\Pictures"
New_Path = XSIUtils.ResolveTokenString( String_Path , 0, false)
LogMessage ( New_Path )


要するに、この ResolveTokenString は [project path] の部分をプロジェクトパスに変換してくれます。


そして、自分のTokenを作ることも出来ます。
やり方は簡単です。配列を2つ用意して、最後に配列を入れればOKです。

例:


// JScript
var tokenNames = ["c","test"]
var tokenValues = ["C:", "\\THIS IS A TEST"]
NewString = XSIUtils.ResolveTokenString( "[c]" + "[test]", 0, 0, tokenNames, tokenValues)
LogMessage (NewString)

//結果: // INFO : C:\THIS IS A TEST



SDK マニュアル:XSIUtils.ResolveTokenString

※ SDKマニュアルに [Project] は [プロジェクト] とか日本語に翻訳されているけど、Softimage様は日本語が分からないのでこのまま使うと何もでてきません。

ユーザーガイドのトークンとテンプレートにトークンリストが書いてあります。



SI | ゆれもの、そしてobj と obj の角度と距離の取得方法

ちょっと前までやっていたプロジェクトでゆれもの作業があったんですが、スペックが低いリアルタイム系なのでシミュレーションとかはNGでした。リアルタイムエンジンのシミュレーション機能というオプションなどありません。全てはFCurveのみというプロジェクトでした。

それでゆれものを100%手動で付けるのは嫌だから、スプラインIK+スプリングを使う事にしました。

syflex も試してみましたが、大変なことになっていたのでやめました。
コリジョンがないけど、どうせ最後に手動で直さなきゃいけないからいいか。

骨にスプラインIKを付けて、このsIKコントローラーにスプリングを付けるというやり方です。
最終的にsIKをプロットして、スプリングを消して、FCurveを手動で編集というやり方。

それで、MTSpring は結構良い感じなんですけど、スクリプトで自動化するのが難しそうだったから SI の Tail を使う事にしました。

MTSpring のHelge Mathee の記事(英語)
MTSpring(Youtube動画)

MTSpring は手動でスプリングを作って、回転とスケールで合わせてスプリングを増やしていくという作業になります。
手動でやってもいいけど、キャラクター何10体もあるとさすがに作業の途中で死にたくなります。

今回もまた似たようなスペックのプロジェクトでまたゆれもの作業があります。今回はもう少し余裕があるので、MTSpringを自動化することに決めました。

説明したとおり、MTSpring は回転とスケールでコントローラーに合わせる方法しかないので、その回転とスケールの自動取得方法が必要となってきます。

それで今日はずっと難しい三角法を使って、色々やってみて惜しいところまでいきましたがコンストレインとカーブを使えば楽じゃん?!と気づきました。処理が遅くてもいいからね、1,2秒ぐらいしか変わらないと思うし。

それで、このファンクションを作りました:


function getRot(objA, objB){
DirectionCnsPath = "D:\\Constraints\\DirectionCnsY.Preset"
var c = objA.Kinematics.AddConstraint(DirectionCnsPath, objB, 0)
DeleteObj(c)
}


D:\\Constraints\\DirectionCnsY.Preset は自分でカスタマイズしたDirection コンストレインのプリセットです。

今回は Y 軸を次のオブジェクトに向かせたかったけど、DirectionのデフォルトオプションはX軸で回転を合わせてくれます。このオプションをスクリプトで変えて、コンストレインを消すとなぜかXに戻ってしまいます。
それで、自分のコンストレインプリゼットを作ることにしました。

そのやり方は簡単です。コンストレインして、好きなようにいじって、Presetファイルに保存。
そして、コンストレインを付ける時にコンストレインの名前じゃなくて自分のプリセットのパスにすれば良い。

よし、次は長さです。

オブジェクトとオブジェクトの距離もまた難しい三角法で色々試していましたが、やっぱりカーブを作って、そのカーブの長さを取得するだけで十分!と気づきました。何時間も無駄にしました。


function getLength(objA, objB){
Ax = objA.Kinematics.Global.posx.value
Ay = objA.Kinematics.Global.posy.value
Az = objA.Kinematics.Global.posz.value
Bx = objB.Kinematics.Global.posx.value
By = objB.Kinematics.Global.posy.value
Bz = objB.Kinematics.Global.posz.value

var crv = SICreateCurve("crvlist", 1, 1);
SIAddPointOnCurveAtEnd("crvlist", Ax, Ay, Az, false, 0, null);
SIAddPointOnCurveAtEnd("crvlist", Bx, By, Bz, false, 0, null);

length = crv.ActivePrimitive.Geometry.curves(0).length
DeleteObj(crv)

return length
}


距離と回転値分かっていれば、あとはこっちのもんですね!


では、また。

SI | FBX カスタムインポーター

仕事のプロジェクトではFBXをSoftimageにインポートする作業がありましたので、
ファイルによって設定が変わったり、その後のデータ整理も自動にしたくて、プロジェクト専用のインポーターを書いてみました。

FBXをインポートすると、色々整理しないといけませんね。
ウェイトは100%ではないところ、不要なプロパティーいっぱい残っていて、クラスター名を直さないといけない、などなど。

まぁ、そこまで難しくはないコードですね。 
(ウェイトは少し厄介だったけど、ウェイトの話はまた今度にしようかな。)

FBXの設定方法分かるまで少し時間が掛かってしまったので、久しぶりのSnippetをメモっておきます。

FBXImportAnimation(false);
FBXImport();


を使えば、アニメーション無しでFBXをインポートが出来ます。

しかし、マテリアルのオプションがありません。
そして、ハードエッジのオプションは 2011 SP1以降から追加されたようです。

色々調べてみたら、どうやら FBX の PPG を作っておけば、FBXImport()はそのPPGの設定を読みんでくれます。

FBX の 設定プロパティを作る方法は:


ActiveSceneRoot.AddProperty( "ImportFBXOptions")


これをオブジェクトにすれば、スクリプトで簡単に設定が出来ます。
FBXのPPGをいじりながらログを見れば、全部のオプションが分かります。

例:

//JScript
var r = ActiveSceneRoot

// FBXOption があった場合、削除しておく
if (r.properties("ImportFBXOptions")) deleteObj(r.properties("ImportFBXOptions"))

// FBX PPG 作成
var fbxPPG = r.AddProperty( "ImportFBXOptions")

// FBX オプション設定
fbxPPG.ImportHardEdges = 0
fbxPPG.ImportCameras = 0
fbxPPG.ImportLights = 0
fbxPPG.ImportMaterialsAndTextures = 0
fbxPPG.ImportAnimation = 1
fbxPPG.ImportSkins = 1

// FBX インポート
var fbx = FBXImport()

// インポートキャンセルされていないと次に進む。
// キャンセルされていたら、強制終了。
if (fbx !=false){
logmessage("FBX Imported")
// Do Something
}

// FBXOption を削除
if (r.properties("ImportFBXOptions")) deleteObj(r.properties("ImportFBXOptions"))




FBXImport() を空っぽのままで使うとFBXのオプションウィンドウが出てきます。

ファイルプロンプトのみにしたい場合はこんな感じで出来ます:

//JScript
var oFile = XSIUIToolkit.FileBrowser
oFile.InitialDirectory = "D:\\myara\\topsecret\\scenes" // デフォルトのフォルダ
oFile.Filter = "FBX (*.fbx)|*.fbx|All Files (*.*)|*.*||"
oFile.ShowOpen()

var fbxImportComm = FBXImport(oFile.FilePathName)


問題はですね、FBXをインポートしている時にProgress Barが出てきません。
そして、FBXImporterはめちゃくちゃログを残すので軽いFBXファイルでもSIが瞑想に入ってしまいます。
他の人が使う時に落ちたか!と少しビックリするかもしれないので、普通の FBXImport() で良いかな。


参考まで

SI | Memo : Python PPG onthefly

自分はPython詳しくないので、昨日(数時間前)スクリプトを書きながら学んだことをメモっておきます:

JScript で オンザフライのPPGを作る時に Logic に function + .toString() をつけるだけで済むから便利ですね。

これはPythonでは toString() みたいな方法はありません。

ちょっと面倒だけど、一応方法あります。

このリンク(英語)を見て、知りました。


一つは「'''」の間にファンクションを入れて、ストリングとしてLogicに入れることです。


sLogic = '''
def test_OnClicked():
#do something
'''

という感じです。

これは VBScript と比べて、さほど不便でもないな。

もうひとつは

TXT にファンクションを書いて、openとread でファイルを読み込んでロジックにする。


f = open("C:/My Documents/callbacks.txt")
s = f.read()
oLayout.Logic = s


そして、JScriptと同じく「\」を使うときに「\\」を書かなきゃいけない。

しかs、「\\」を「'''」の中に入れると、x2にしなきゃいけない!

普段書いてる「\\」を「\\\\」にすること。

つまり「\」を書きたいときに「\\\\」を書かなければなりませんということです。
合計x4ですね。


そうしないと、「EOL while scanning string literal」という良く分からんエラーが出てしまいます。

PPG OnTheFlyの例はこんな感じ:

from win32com.client import constants as c
xsi = Application
oProp = XSIFactory.Createobject( 'CustomProperty' )
oProp.name = "Test"

oLay = oProp.PPGLayout
oItem = oLay.AddButton("Run", "Run Test")
oItem.SetAttribute( c.siUICX, 315 )
oLay.Language = "Python"

sLogic = '''

def Run_OnClicked():
log = Application.LogMessage
log ("Python TEST")
log ("\\\\")
'''
oLay.Logic = sLogic
xsi.InspectObj( oProp )

# こうなるはず:
# INFO : Python TEST
# INFO : \

SI | Memo : GetICEAttributeFromName("PolygonPosition")

仕事関係で古いSIのプロジェクトもあったりするから、自分のツール開発ではICEを避けていました。

最近は古くてもXSI7になるにもかかわらずICEを避けつづけていました。
7ではICEのモデリングはあんまり出来ないしね。でもこのコマンドは7からでもOKだったことは知りませんでした。

これからはこのGetICEAttributeFromNameを使ってみます。

未完成の mSymm2 プラグインはまだいじってる為このコマンドを試してみました。
GetICEAttributeFromNameってのは、ICEアトリビュートを名前で取得するという意味です。
そのAttributeは色々ありますが、今回はPolygonPositionを使ってみました。

この「PolygonPosition」でポリゴンの位置情報を簡単に取得ができます。

今まで

obj.Geometry1D.CenterPosition

を使っていました。遅くて非常にややこしいやり方ですが、これしか知りませんでした。
AFAIK、ICEなしだとこれしかないですね。


例えば、X<0 のポリゴンを選択するファンクションはこんな感じに作っていました:

 
var oObj = selection(0)

// ポリゴンコレクションを取得
var CompCol = oObj.ActivePrimitive.Geometry.Facets;
var CompColCount = CompCol.Count;

// ターゲットのポリゴンコレクションを作っておく
var oTargetPoly = oObj.ActivePrimitive.Geometry.CreateSubComponent(siPolygonCluster);

// Geometry2D でポリゴンを取得
var oGeometry = oObj.obj;
var o2DComponent = oGeometry.Geometry2D;
var oPos = XSIMath.CreateVector3();

for ( var i=0; i < CompColCount; i++ ) {
var idx = CompCol(i).index;

// Geometry2DのCenterPositionでポリゴンの位置を取得
o2DComponent.CenterPosition(idx, oPos);

if (oPos.x < 0){oTargetPoly.Addelement(idx)};
}
SelectObj (oTargetPoly);



面倒だし、前回メモった「GetGeometry2」も使えなくて、元のポリゴンの位置取得が出来ません。

ICEなしでどうやってやるのかは分からないけど、ICEのPolygonPositionを使うと楽に出来ます:


 
var oObj = selection(0)

// ジオメトリ取得
var geo = oObj.ActivePrimitive.GetGeometry2(null, 0)

// ポリゴンの位置情報を取得
// JScriptだと .toArray() を使わないといけない。面倒くさい。
var polys = geo.GetICEAttributeFromName("PolygonPosition").DataArray.toArray();
// lenghtを取得しておく。(CollectionじゃなくてArrayだから)
var polyslength = polys.length

// ターゲットのポリゴンコレクションを作っておく
var oTargetPoly = oObj.ActivePrimitive.Geometry.CreateSubComponent(siPolygonCluster)

// ポリゴンの位置情報のアレイをループ
for ( var i=0; i < polyslength; i++ ) {
if (polys[i].X < 0){oTargetPoly.Addelement(i)}
}
SelectObj (oTargetPoly)


GetGeometry2 を使える上に、速度も倍ぐらい速くなっています。

追記:因みに、Pythonだとこんな感じです:

 
x = Application
from win32com.client import constants as c
oObj = x.selection(0)
geo = oObj.ActivePrimitive.GetGeometry2(0, 0)
polys = geo.GetICEAttributeFromName("PolygonPosition").DataArray
oTargetPoly = oObj.ActivePrimitive.Geometry.CreateSubComponent(c.siPolygonCluster)
for i, pos in enumerate(polys):
if (pos.X < 0):
oTargetPoly.AddElement(i)
x.SelectObj(oTargetPoly)

やっぱりPythonって見やすいね。このコードだとJSより 40~50% 遅いけど。
Next page