19.9.09

ヘアースタイリング - Hair2Curve

4 comment
ヘアースタイリングツールを作ってみました。
初めに断っておきますが、このツールは、スクリプテッドオペレータを使用しています。
で、そのスクリプテッドオペレータが存在している状態で、保存すると二度とデータは復帰出来ません
という超危険な仕様なので、使い方は気をつけてくださいw
保存するときは、ヘアについてる、Hair2Curveをすべて消してからにしましょう。

使い方は、下記画像さんしょ。



ヘアを選択して実行するやつ
Hair2Curve.pys

#---------------------------------------------------
app=Application;log=app.LogMessage;sel=app.Selection
import win32com.client
#---------------------------------------------------
if sel.Count==0 or sel(0).type!="hair":
raise

sCode = """\
from win32com.client.dynamic import Dispatch as d
def Hair2Curve_Update(ctx,OutPrim,InPrim):
oCrvCol = InPrim.Value.Geometry.Curves
lHairPos = [[],[],[]]
for oCrv in oCrvCol:
for fHairPer in [f/13.000*99.999+0.001 for f in range(14)]:
vPos = d(oCrv.EvaluatePositionFromPercentage(fHairPer)[0])
lHairPos[0].append(vPos(0))
lHairPos[1].append(vPos(1))
lHairPos[2].append(vPos(2))
OutPrim.Value.GetGeometry2(ctx.CurrentFrame).Points.PositionArray = lHairPos
"""

oCrvCol = win32com.client.Dispatch("XSI.Collection")
for oHair in sel:
oHair.AllowStretch.Value = True
lPos=[[f for f in l] for l in oHair.ActivePrimitive.Geometry.Points.PositionArray]
iStrNmb=len(lPos[0])/14
lPos.append(list([1])*len(lPos[0]))
oCrv = oHair.Parent.AddNurbsCurveList()
for i in range(iStrNmb):
oCrv.ActivePrimitive.Geometry.AddCurve(
[ lPos[0][i*14:i*14+14],
lPos[1][i*14:i*14+14],
lPos[2][i*14:i*14+14],
lPos[3][i*14:i*14+14] ],
range(14),
False,
1,
1
)
oFitCrvOp = app.ApplyGenOp("CrvFit", "", oCrv, 3, "siPersistentOperation", "siKeepGenOpInputs", "")(0)
oFitCrvOp.points.Value = 3
log(oFitCrvOp.Parent3DObject)
oFitCrv = oFitCrvOp.Parent3DObject
oHair.Parent.AddChild(oFitCrv)

oTransfo = oHair.Kinematics.Global.Transform
oFitCrv.Kinematics.Global.Transform = oTransfo
app.FreezeModeling(oFitCrv)
app.DeleteObj(oCrv)
#app.ToggleVisibility(oFitCrv)
oCrvCol.Add(oFitCrv)
app.Refresh()

oOpr = oHair.ActivePrimitive.AddScriptedOp(sCode,oFitCrv.ActivePrimitive,"Hair2Curve","Python",1)

sel.SetAsText(oCrvCol.GetAsText())


そんでもって、Hair2CurveOpを消すスクリプトは、これです。

FreezeHair2Curve.pys

#---------------------------------------------------
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
#---------------------------------------------------
app.ActivateObjectSelTool()
oHairPrimCol = app.FindObjects("","{D5C3CDBA-B361-4A11-9582-91DBA0ECBB49}")
for oPrim in oHairPrimCol:
oOp = oPrim.NestedObjects("Hair2Curve")
if oOp:
log(oOp)
oCrv = d(oOp).InputPorts(0).target2.Parent3DObject
app.FreezeObj(oPrim.Parent3DObject)
app.DeleteObj(oCrv)


いやいや、おっかねぇスクリプトですね。
会社でなんて使わせられません。

そもそも、Hairにスクリプテッドオペレータが適用出来ないXSI様が悪いんですけどね。
HairにICE適用出来ないのも悪いですね。

そいや、Softimage2010を使い始めましたわ。
2010になって、HairにICE適用出来るようになったのかしら。
後でやってみよーっと。

FaceRobotは、なかなか素晴らしいものがありますね。
顎とか、口まわりとか、目まわりとか。
顔は、モーションキャプチャの場合、Envelopeのほうが断然楽ですな。
ただし、後付け修正したいので、Shapeで加えるとかですかね。
もちろん、移動差分の骨のクリップを並べて、MixerのNormalizeをオフって加算でやるのもいいですね。
どっちも得手不得手があるので、それぞれ選ぶべきですなー。

データは軽く作れよっ。

26.7.09

Sukio Sukio Sukio Python X

0 comment
遂に10回目になりました。
しかし、かなり時間を空けてしまいましたね・・・。
サボタージュ期間が長かったかもです。
でも、海の日にヴぁかんスに行ってきたのでちょっと回復。
さぁ、行きましょうか。

では、今回は、よく使うPythonの関数を紹介しましょう。
いろいろ便利なものが揃っています。
取りあえずは、こんなのあるなーくらいに思うくらいにしておいて、確かこんなのあったな。
って時に使うのがベストですね。


map(関数,シーケンス)

これは、シーケンスのひとつずつのアイテムを関数にかまします。
戻り値は、関数のreturn値を介してリストで戻します。

mapを使ったもの

def fct(p):
return p
lRtn = map(fct,range(10000000))


for文を使ったもの

lRtn = list()
for i in range(10000000):
lRtn.append(i)


内容はほぼ一緒です。(あんまり意味の無いサンプルのサンプルですが・・・)
ただmapを使ったほうがちょびっとだけ早く動作するみたいですね。
XSIでの使い方は、大量のオブジェクトがあったとして、そのひとつひとつに同じ関数をかましたいときに使います。
例えば、null群があって、そのsizeを0.1にしてさらに形状をBoxにしたいときは

def SetDefaultNull(pObj):
pObj.size.Value = 0.1
pObj.primary_icon.Value = 4
map(SetDefaultNull,Application.Selection)


こんな風に使います。
やりたいことをファンクションにしておいて、シーケンスをかます。
とてもシンプルに書けます。


enumerate(シーケンス)

enumerateは、for文と共に使います。
インデックスとアイテムを同時に発生させることが出来ます。

for i,o in enumerate(list):

iにインデックスが入って、oにリストの中身が入ります。
for文だけでリストなどを回すとリストに入っているアイテムしか取得出来ず、それが何番目のオブジェクトなのかは分かりません。
使いどころは例えば、envelopeのweightsとdeformersをシンクロしたいときに効果を発揮します。weightsは、デフォーマの順番に各ポイントが割り振られている2次元配列で戻って来ます。

((0.0,50.0,100.0),(100.0,50.0,0.0))←こんな感じに。

つまり、weightsの0番目に処理しているときに、deformersの0番目を取得したいときなどに使えますね。

import win32com.client
app=Application;log=app.LogMessage;sel=app.Selection
oDfm = sel(0).Envelopes(0).Deformers
tWgt = sel(0).Envelopes(0).Weights.Array
oBoneCol = win32com.client.Dispatch("XSI.Collection")
for i,w in enumerate(tWgt):
if max(w):oBoneCol.add(oDfm(i))
log(oBoneCol.GetAsText())


このサンプルは、エンベロープのウェイトが0のオブジェクトを弾くXSICollectionを作成します。
max()も使っていますが後述します。
maxの値が0ってことは、weightがすべて割り振られてない0 weiht bone なので、スルーです。


sum(シーケンス)

リスト、タプルの足し算を返します。
sum(range(10))は、0から9を足しているので45を返します。
ただし、数字しか受け付けません。文字列の場合は、joinを使いましょう。

",".join(string_list)


よく使うとか言っておきながらサンプルを思いつけませんでした(ギャボンヌ)
うーん。使わないかなw


max(シーケンス),min(シーケンス)

maxは、リストなどの最大値を取得。minは、同様に最小値取得です。
FCurveの最大値とか取れそうですね(取ったこと無いけど・・・)

app=Application;log=app.LogMessage;sel=app.Selection
log(max([k.Value for k in sel(0).posy.Source.Keys]))



open(ファイルパス,モード(読み取りとか書き込みとか))

結構良く使います。
weight情報を吐き出したり、ログファイルを生成したいときとか、テキスト情報を読みに行ったりとか。
読み込みたいときはコレです。stripは、末尾の改行を示す"\n"を取り除いてくれます。
pathになんか入れてください。

app=Application;log=app.LogMessage
f=open(path,"r")
while 1:
s = f.readline()
if not s:break
log(s.strip())
f.close()



逆に書き込みたいときは

app=Application;log=app.LogMessage;sel=app.Selection
f=open(path,"w")
f.write(sel.GetAsText())
f.close()


pathになんかパス。選択したもののリストを書き出します。
と、Openには、いろいろあるんですが、使うのはこれくらいで十分じゃないでしょうかね。


今回は、こんな感じです。
だいぶ、Pythonの海泳げるようになって来たんじゃないでしょうか?
そいや、初心者の講座って始めたんですが、全然初心者向けにならなくなってきた感じですよね。
一度初心者の道を通り終わると、初心にかえるのが難しいことを意味しているんですが、ということは、ワタクシもまだまだひよっこなんだなぁと思い知らされます。

絶えずもどかしい。ああもどかしい。もどかしい。

でも、ライブラリとして、とって置けたのでなんとなくはいいかな。と。
完全なる自己満足。

ってなわけで、次回は、HairをCurveに変換するPythonでも公開しましょうかね。
Curveに変換して、Scripted Operatorにつなぎます。
すると、Curveで、Hairをスタイリング出来ます。

Softimageは、Hairスタイリングツールは、開発者のためにあるようなものですから、ちょっとは楽にしてほしいところなんですけどねぇ・・・。
とはいえ、この辺は難しいところですから、デザイナーがどう使うかにかかっているのですが。

では、近々公開しますね。

6.4.09

Sukio Sukio Sukio Python - IX

0 comment
さて、9回目です。
ちょっと難しい問題に取り組んでいきます。

それでは、正規表現2回目。
defを用いた置換を紹介します。

app = Application;log = app.LogMessage
import re
def repl(m):
dictRepl = {
"Model":"A",
"Model1":"B",
"Model2":"C",
"sphere":"maru"
}
try:
return dictRepl[m.group()]
except:
return m.group()

for oObj in app.Selection:
s = "%s -> %s"%( oObj.FullName,re.sub("\w+",repl,oObj.FullName) )
log(s)

こんな感じの時に実行しています。



このコードは、辞書を使ったパターン置換です。

dictRepl = {
"Model":"A",
"Model1":"B",
"Model2":"C",
"sphere":"maru"
}

この辞書の部分で、

Model  → A
Model1 → B
Model2 → C
sphere → maru

という置換の定義をする事にしています。
法則というか決まりごとですね。

そして、置換するメソッドとしてsubを使用しています。
re.sub("\w+",repl,oObj.FullName)

第1引数は、拾うパターン。
第2引数は、置換する文字列。またはファンクション。
第3引数は、置換する対象です。

第1引数の \w+
これは、[A-Za-z0-9_]と同じになります。
A~Z、a~z、0~9と_にヒットします。
つまり、XSIの場合"."ドットまで、ひとくくりです。

第2引数は repl
通常は文字列ですが、defを使用出来ます。
上記は、replファンクションが入ります。
このファンクションには、引数として、マッチオブジェクトが入る事になっています。
1でヒットしたマッチオブジェクトです。

return dictRepl[m.group()]

そして、上記replのmに引数が渡され、辞書の値を返します。

例えばこのようにキーを入力すると、値を取ることが出来ます。
dictRepl["Model"] -> "A"

Model なら A が返ってきます。

それと、新たにtry except文を入れてみました。
これは、エラーを吐いても例外に飛ぶことが出来る文です。
try内で、エラーが発生したら、except(例外)に飛びます。
マッチオブジェクトが、Model3というものにヒットした場合、無い辞書のキーを取ってしまうとエラーになります。

dictRepl["Model3"]

これは、エラーですので、回避して何も変換しないという

m.group()

を返すことにしています。

今回は、あまり利用価値が無いものかもですが、一応メモ的な感じです。
応用としては、ターゲットの指定のときに役に立つかなぁとも思ってます。
コンストとか、Definition指定するの面倒ですものね。

とはいえ、正規表現はこの位で終わりますね。
そういえば、JScriptでも同じようなことが出来ますから、別にPythonでなくてもいいんですけどね。
連想配列で検索です。
VBは知りません。

ではでは。

16.2.09

Sukio Sukio Sukio Python - VIII

0 comment
第8回目は、正規表現に行きたいと思います。
正規表現は、とても難しいですので、ワタクシも全て理解しているとは言いがたいです。
ですが、少しでも学ぶことが出来れば、強力なツールになること間違いなしです。
正規表現は、パターンマッチをするには、最強です。
では、そのほんのちょっとしたさわりをご紹介します。

例えば、duplicateなどした場合には、cube1など、お尻に数字が入ります。
さらに100個くらい増やした後、幾つかのcubeをABCやguideなどと数字は生かしたままりネームしてしまったとします。
最悪な事に、幾つかのオブジェクトはDeleteしてあります。
その後、そのオブジェクト達の番号に対応するnullを用意しなくてはならない場合があるとします。
さらに、nullをリグとするposeコンストをしたいです。
cubeとABCとguideは、番号続きであるもののごちゃ混ぜ。
さらにnullには、その番号に対応した数字を入れなくてはなりません。

cube1
guide3
ABC24
cube54
guide98
ABC145
cube203
...

数字を取得するのなら、cubeやABCやguideを抜いたら良いですね。
ですが、この場合の発想は逆で、数字だけを抜き取ります。
こういったときには、正規表現を使うのが一番です。

まず、正規表現を使うことが出来るreモジュールをインポートします。

import re

これで、reモジュールを使うことが出来ます。
reには幾つかのメソッドが存在します。
まずは、compile
パターンオブジェクトを作成します。

reD = re.compile(r'\d+$')

\d+

ここで使われている特殊な文字『\d』は数字を意味します。
digitのdですね。
+を付けると、0や567など1以上の複数にマッチします。

$

これは、末尾を意味します。

\d+$

としておけば、末尾が数字のものという意味になります。
次にパターンにひっかかるかどうかのマッチオブジェクトを作成します。
ここでsearchを使います。

m = reD.search('cube54')

mにマッチオブジェクトが入りました。
そして、実際にマッチングしている文字列を取り出すには

m.group()

とします。
これで

54

が取得出来ます。
選択して、それに対応するnullを作成しposeコンストするにはこんな感じにします。

import re
app = Application
log = app.LogMessage
reD = re.compile(r'\d+')
for oObj in app.Selection:
m = reD.search(oObj.Name)
if m:
oNull = app.ActiveSceneRoot.AddNull('null%s'%m.group())
oObj.Kinematics.AddConstraint('pose',oNull)


これにより、数字に対応したnullがposeコンスト付きで出来ることでしょう。

もう少し複雑にしましょう。
例えば、カメラモデル名にシーン番号カット番号イン点アウト点などを表記させておきます。

s021c156_Camera_231_310

このパターンは、かなりの数に上ります。
通常の考え方で、カット番号を取得したい場合は
カメラのモデルをoCamMdlとした場合

oCamMdl.Name[5:8]

とすれば『156』を取得出来ます。
しかし、こうなればどうでしょう?

s1c56_Camera_231_310

『_Ca』が取得されてしまいます。
これではあまりに情けないので、正規表現を使って取得してみることにしましょう。

パターンオブジェクトを作成します。

reCam = re.compile(r'(s)(\d+)(c)(\d+)(_Camera_)(\d+)(_)(\d+)')

()を付けるとくくられて、グルーピングされます。これは、後述します。
上記を日本語で書くとこんな感じでしょうか。

(sにマッチ)(数字にマッチ,+で複数も可)(cにマッチ)(数字にマッチ,+で複数も可)(_Camera_にマッチ)(数字にマッチ,+で複数も

可)(_にマッチ)(数字にマッチ,+で複数も可)

次に、パターンオブジェクトに対して、実際に判定する文字列を入れマッチオブジェクトを作成してみます。

reCam = re.compile(r'(s)(\d+)(c)(\d+)(_Camera_)(\d+)(_)(\d+)')
m = reCam.search('s021c156_Camera_231_310')

mにマッチオブジェクトが入りました。
そして、実際にマッチングしている文字列を取り出すには

m.group()

とします。
これで

s021c156_Camera_231_310

が取得出来ます。
ここまではあまり意味無いですが今度は

'Camera_root'

を入れてみます。

m = reCam.search('Camera_root')
m.group()

これはエラーになってしまいます。
パターンにひっかから無かったからです。
searchで、パターンにマッチ出来ないと、Noneオブジェクトが返ってくる仕組みになっています。
ですので

if m:
m.group()

などとすれば、マッチしているかどうか判定出来ます。
ifで、Noneは、Falseと判定されます。
次に、マッチオブジェクトに対して、groups()としてみましょう。

import re
app = Application
log = app.LogMessage
reCam = re.compile(r'(s)(\d+)(c)(\d+)(_Camera_)(\d+)(_)(\d+)')
m = reCam.search('s021c156_Camera_231_310')
if m:
log(m.groups())


結果は、()でくくったグルーピングをタプルとして返します。

('s', '021', 'c', '156', '_Camera_', '231', '_', '310')

カット番号を取りたい場合は決まって4番目の値をとりたいので

m.group(4)

とすれば、目的のカット番号

156

が取得出来ますね。

ここでもう少し便利にすることが出来ます。
パターンに名前を付けることが出来たりもします。

import re
app = Application
log = app.LogMessage
reCam = re.compile(r'(s)(?P<scn>\d+)(c)(?P<cut>\d+)(_Camera_)(?P<in>\d+)(_)(?P<out>\d+)')
m = reCam.search('s021c156_Camera_231_310')
if m:
log(m.group('scn'))
log(m.group('cut'))
log(m.group('in'))
log(m.group('out'))

このように

(?P<scn>\d+)

とすればscnという名前から取得出来るようにタグを付けておいて、後で呼び出すときに指定できるようにすることも可能です。

このように、正規表現は1冊の本として出ることが許されるほど複雑怪奇です。
ですが、webには、沢山の例がありますので、少しずつ覚えて行けばOKです。
使えなくても対した問題にはなりませんが、使えるようになれば、更なる進歩が望めます。

次回ももう少し正規表現の世界にダイブしてみることにしましょう。
では、しばらくの沈黙の後に。

5.1.09

Sukio Sukio Sukio Python - VII

1 comment
今年もよろしくおねがいします。
7回目になりました。はじめるときは、ココまで続けると思ってませんでした・・・。
教えるとなるといろいろあるものですね。
そうそう、Pythonのことをもっと勉強しようと思いまして、Pythonクックブックという本を買ってみました。
で・す・が、物凄く難しいですねぇ。
というか、本当に一部分の機能しか使っていないんだなということを、痛感させられます。
Pythonが使用されているのは、多岐多用に渡っていますからそれはそうなんですけどね。
CGで使うのは、本当に基本的な機能だけです。
ですが、基本的な機能さえ覚えてしまえば、ほとんど応用できますね。
あとは、CGのアルゴリズムをPythonに組み込めば、立派なスクリプターになれると思います。
バネ係数とか、波紋の伝達の式とかとかとか。
まだまだ勉強しなくてはならないものは、たくさんあります。

ではでは、前回の回答からしますね。
モデルの名前は、LR反転することなくオブジェクトの名前だけで反転するコードです。

import string
app = Application
table = string.maketrans('LR','RL')
lSel = list()
for oObj in app.Selection:
sMdl = oObj.Model
sName = oObj.Name.encode().translate(table)
lSel.append('%s.%s'%(sMdl,sName))
app.Selection.SetAsText(','.join(lSel))

テキスト処理する際、最速になる傾向は、分解・変換・結合です。
細かく割って、変換した後、結合させる。
という方法が、早く動作する傾向にあります。

for oObj in app.Selection:

この行で、セレクションのリストを順々にoObjに代入します。
選択しているものが

ROB.IK_Rarm, ROB.IK_Rfoot, ROB.IK_Rhand

だとすると一番最初に代入されるのは

ROB.IK_Rarm

処理が終わって、2番目に代入されるのは

ROB.IK_Rfoot

となります。

sMdl = oObj.Model

モデルの名前は、変換させたくないので、一度sMdlという変数にモデルをキープしておきます。

ROB.IK_Rarm

というオブジェクトでしたら、ROBという文字列が入る事になります。

sName = oObj.Name.encode().translate(table)

次にモデルの名前だけ前回のやり方で翻訳します。
上記でしたら

"IK_Larm"

に変換されているはずですね。

lSel = list()
lSel.append('%s.%s'%(sMdl,sName))

一度、for文の前に、lSelという空のリストを作っておきます。
その後、appendメソッドで、リストに追加します。
その前にPython特有の文字列処理があります。

'%s.%s'%(sMdl,sName)

上記の例ですと

sMdl="ROB"
sName="IK_Larm"

ですので"."(ドット)が挟まり

"ROB.IK_Larm"

になります。
%sに、文字列が次々に代入されるという書き方です。

sMdl+'.'+sName

と書きたくなりますが、%sを使った書き方のほうが、高速に動作します。
覚えておいて損は無いですよ。

'%s'*3%('a','b','c')

と書けば

"abc"

となります。文字列にも掛け算が出来るのもPythonの特徴です。
ひとつの文字列を入れたい場合は、タプルにしなくてもOKです。

s='XSI'
'I love %s' % s



"I love XSI"

こんな感じですね。

そして、次々に処理をしていき、リストlSelにappendで追加していきます。
例えば

lSel=[ "ROB.IK_Larm" ]
lSel.append("ROB.IK_Lfoot")



[ "ROB.IK_Larm", "ROB.IK_Lfoot" ]

こんな感じです。
最後には、内部的には、こんな感じで格納されているはずです。

[ "ROB.IK_Larm", "ROB.IK_Lfoot", "ROB.IK_Lhand" ]

最後にリストを単一の文字列に結合です。

app.Selection.SetAsText(','.join(lSel))

string.join(list)という形で、listの間にstringを挟んで行ってね。
と命令します。
上記は、リストの間に、','を挟みます。結果は

"ROB.IK_Larm,ROB.IK_Lfoot,ROB.IK_Lhand"

という単一の文字列になり、app.Selection.SetAsTextでテキスト通りに選択しなさいという命令文で完成です。

モデルが無い場合でも、Scene_Rootというモデルがあるので、安全に動作するはずです。
しかし、まだまだ落とし穴はあるもので、NameにもLRが入っているものには、使うことが出来ません・・・。
例えば

ROB.ArmOffset_Rot_R

だとか

ROB.ROB_Rarm

など名前を付けられてしまうともうお手上げですね。
なので、名前の仕様を決めたほうがいいでしょう。
LR以外は小文字でお願いね。とかですね。

という感じで、今年もがんばって更新したいと思います。
もうそろそろネタ切れかなーと思いきや、実はまだあったりするのでちょっと難しいところに行ってみたいと思います。
正規表現をちょろりと。
これは、本当に難しいので、一度では全然理解できないと思いますが、少しでも使えるようになるとかなり強力な力を発揮するので、少しずつ覚えておくのが、◎です。
いろいろなサイトもありますが、まずはさわりだけやりたいと思います。

ではでは、しばらくの時を経て、次回をおたのしみに☆


10.12.08

Sukio Sukio Sukio Python - VI

2 comment
さてさて、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'になってしまいます・・・。
目標が見つからなくエラーが出てしまいます。

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

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

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

28.11.08

XSI 7.01 Released.

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

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が出るまで我慢だなぁ。