2014年01月

2014年01月31日

ACSを使ってみた その48 modo701 SP2

前回まででトランスフォームコントローラの反転までが出来るようになった。今回はさらにIK-FKの切り替えとかの設定を左右反転させてみたい。

これらの設定は左右にある6つのアイテムにユーザーチャンネルとして割り当てられている。

fig01

例えばこれは手の部分にあるコントローラのユーザーチャンネル。

fig03

アイテムをクリックすると「rgk_selcmd_chanctrl」が起動して、プリセットの「Popover Style Channel Controls」の設定によって「item.ChannelPopover」か「item.channelHaul」のどちらかで下のようにこれらがパネル表示される仕組みだ。

fig02

ここには指の曲がり具合や開き具合なんかが入っている。

タグを調べてみると、「u」の項目があって、後ろの「#」や「=」の数を数えてみると、有効なチャンネルの数と一致した(「x.........」とか「v.......」とかはラベルとして使ってるだけでチャンネルとしては使われていない)。

fig04

アイテムに付いているチャンネルのどこからユーザーチャンネルなのかを調べる方法がよくわからなかったんだけど、この「#」と「=」の数を数えてその回数ぶんだけ全チャンネル数−1からチャンネル番号を遡って行けばユーザーチャンネルの開始位置に辿りつけそうな感じだ。もちろん「x.......」などのラベルチャンネルはカウントしないでスキップしなくちゃならないけど。

左右3つずつのコントローラを調べた限りではチャンネルの62番目からがユーザーチャンネルになっていたので今回はそのまま62と言う数値をハードコーディングしちゃった。以下がその部分。「RGCH」タグの値に「u」が入っている場合に実行される。

まず左右のユーザーチャンネルの名前と値のセットを記録するために「plist1」「plist2」を空のリストで用意して、アイテムのチャンネル数を取得する。アイテムのチャンネル数を取得するクエリ「channel.N」は直前にクエリしたアイテムに対してチャンネル数をクエリするようになっているのでまずアイテムの名前をクエリして調べるアイテムを設定し、続けてチャンネル数をクエリして取得している。そして62番目からチャンネル数の最後までをfor文で巡回して、チャンネルの名前とチャンネルの値を取得してそれを「plist1」「plist2」に記録する。あとはラベルになっている「x..........」みたいなチャンネルを除いて左右のチャンネルの値を入れ替えてセットした。

upos=rgch.find("u")
if upos!=-1:
 plist1=[]
 plist2=[]
 lx.eval('query sceneservice item.name ? %s' % id1)
 n=lx.eval('query sceneservice channel.N ?')
 for i in range(62,n):
  cname = lx.eval('query sceneservice channel.name ? %s' % i)
  cvalue = lx.eval('query sceneservice channel.value ? %s' % i)
  plist1.append([cname,cvalue])

 lx.eval('query sceneservice item.name ? %s' % id2)
 for i in range(62,n):
  cname = lx.eval('query sceneservice channel.name ? %s' % i)
  cvalue = lx.eval('query sceneservice channel.value ? %s' % i)
  plist2.append([cname,cvalue])

 lx.eval('select.item %s mode:set' % id2)
 for i in range(len(plist1)):
  if not '..........' in plist1[i][0]:
   lx.eval('item.channel %s %s' % (plist1[i][0],plist1[i][1]))

   lx.eval('select.item %s mode:set' % id1)
 for i in range(len(plist2)):
  if not '..........' in plist2[i][0]:
   lx.eval('item.channel %s %s' % (plist2[i][0],plist2[i][1]))

これがその実行結果。足のIK−FKが入れ替わって地面から浮いている方の足はどちらのポーズでもIKコントローラが表示されていない事がわかる。

fig05

ここまでの部分はアップロードしておいたよ。

それではまた来週。

modo701ブログ目次



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG

2014年01月30日

2014の新機能を調べてみた その76 3dsmax 2014

今回は2014用エクステンションのPythonスクリプトについて調べてみたい。

どうも仕組み的に見て、PythonスクリプトはMAXスクリプトから起動はするけど、相互の関係としてはPython側からMAXスクリプトを呼び出して使うのが主な使い方ようだ。

と言うのもMAXスクリプト側からPythonを呼び出す場合、以前にちょっと調べたけど、Pythonスクリプトの実行結果として返せるのがスクリプトの動作が成功したかどうか程度で、様々な値をMAXスクリプトに返す方法は用意されていないからだ。

それに対してPython側からMAXスクリプトを呼び出した場合は「FPValues」という形にパッケージングされていろんな値が取得出来るようだ。

<FPValues> = MaxPlus.Core.EvalMAXScript(<string>)

res = MaxPlus.FPValue()
<bool> = MaxPlus.Core.EvalMAXScript(<String>,res)

PythonからMAXスクリプトを呼び出す時は上記2つの方法が使える。上の方法ではエラーが発生した時は例外を発生する。下の方法ではFalseが返されてFPValueにエラーメッセージが入る。

例えば球体オブジェクト「Sphere001」、「Sphere002」、「Sphere003」を作っておいて、下のコードを実行するとMAXScriptから「CommandString1」に入ったPythonスクリプトが実行される。

CommandString1 =
"
import MaxPlus
res = MaxPlus.FPValue()
status = MaxPlus.Core.EvalMAXScript('$Sphere001',res)
print 'status : ' , status
print 'FPValues Type Number: ' , res.GetType()
print 'FPValues Type Name : ' ,MaxPlus.FPTypeGetName(res.GetType())
print 'FPValues Value : ' ,res.Get()
"
python.execute commandString1

実行するとリスナーにこのように表示される。

fig01

「EvalMAXScript」が「$Spahere001」の評価に成功すると「status」にTrueが返って来ている。そして評価結果はFPValueクラスのインスタンス「res」に格納される。

「FPValue」のインスタンスに何が格納されているかは「GetType( )」メソッドによって調べる事が出来る。ただしその結果は数値として返って来るのでよくわからない。「FPTypeGetName( )」メソッドはその数値を名前に変換するもので、今回の17番は「Node」という名前に変換された。そして「FPValue」に格納されている値は「Get()」メソッドで取得できる。

この「FPValue」のタイプは「FPTypeConstants」クラスに定数としてまとめられているのでNodeが17番だって知らなくても

res.GetType() == MaxPlus.FPTypeConstants.Node

みたいな式で「FPTypeConstants」の中から「Node」にあたる定数を取り出してきて調べる事が出来る。

同様に「EvalMAXScript( )」の引数が1つの場合は「FPValue」が戻るだけだ。

CommandString1 =
"
import MaxPlus
res = MaxPlus.Core.EvalMAXScript('$Sphere001')
print 'FPValues Type Number: ' , res.GetType()
print 'FPValues Type Name : ' ,MaxPlus.FPTypeGetName(res.GetType())
print 'FPValues Value : ' ,res.Get()
"
python.execute commandString1

実行するとリスナーにこのように表示される。

fig02

ここで例えば実行させるMAXScriptの内容を「$Sphere*」にすると、シーン内にイ作った「Sphere001〜003」がマッチするんだけどこれがうまく「FPValue」に変換ができずに例外が発生してPythonスクリプトが終了してそれを実行していたMAXスクリプトも終了して例外発生の表示が出る。

fig03

同様の事を最初に出てきた「EvalMAXScript( )」の引数が2つのもので実行すると、例外は発生せず、そのかわりに戻り値が「False」になって、「FPValue」のタイプが「Str(文字列)」になって、エラーメッセージが格納される。

fig04

ちなみに「$Shere*」はそのままでは戻せないけど、「$Sphere* as array」として配列に変換すれば戻せるよ。その場合Nodeのテーブルになるのでタイプは「Node」じゃなくて「NodeTab」になる。

fig05

取得した「NodeTab」は「Get()」メソッドで取り出してやればその中の要素に個別にアクセスできる。

CommandString1 =
"
import MaxPlus
res = MaxPlus.FPValue()
status = MaxPlus.Core.EvalMAXScript('$Sphere* as array',res)
print 'status : ' , status
print 'FPValues Type Number: ' , res.GetType()
print 'FPValues Type Name : ' ,MaxPlus.FPTypeGetName(res.GetType())
print 'FPValues Value : ' ,res.Get()
objs = res.Get()
for obj in objs:
 print obj
"
python.execute commandString1

fig06

ちなみにPythonではコードのグループ化を字下げ(インデント)の位置で決めている。例えばfor文などで繰り返すコードのグループはそのfor文より字下げされている部分が範囲になる。例えば下のコードはfor文が繰り返し実行するのは「print i」「print i * 2」の2行になる。

for i in range(10):
 print i
 print i * 2
print "finish"

MAXScriptではコードを「( )」でくくってブロック化してたけど、Pythonではそれを字下げで行うので、同じブロックのコードは字下げ位置を揃える必要があるよ。

それではまた次回。

maxまとめページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)3ds Max | CG

2014年01月29日

ACSを使ってみた その47 modo701 SP2

前回は中心にあるトランスフォームのコントローラについて回転角度を左右反転するところまでやってみた。今回はそれを広げて位置もひっくり返して、ついでに対称になっているコントローラも処理してみたい。

まずは位置のひっくり返しを追加した中心にあるコントローラの処理。位置は「RGCH」タグの値に「p〜〜〜」が入っていて、位置で反転させるチャンネルはX軸のみなので、「p」の後ろの文字が「#」になっているものが対象だ。だから「find( )」メソッドで「p#」を検索して−1じゃないものを探してそのXチャンネルの値を読み出し、符号を反転させてセットし直せばOKだ。

lx.setOption( "queryAnglesAs", "degrees" )
#For Center Items
for item in ACS_Items.keys():
 if ACS_Items[item][1] == None:
  id = ACS_Items[item][0]
  tagtypes = lx.evalN('query sceneservice item.tagTypes ? %s' % id)
  if "RGCH" in tagtypes:
   lx.eval('select.item %s mode:set' % id)
   rgch = lx.eval('item.tag mode:string tag:"RGCH" value:?')
   ppos=rgch.find("p#")
   if ppos!=-1:
    px=lx.eval('item.channel pos.X ?')
    lx.eval('item.channel pos.X %s' % -px)

   rpos=rgch.find("r")
   if rpos!=-1:
    rotflags=rgch[rpos+1:rpos+4]
    ry=lx.eval('item.channel rot.Y ?')
    rz=lx.eval('item.channel rot.Z ?')
    if rotflags[1]=='#':
     lx.eval('item.channel rot.Y %s' % -ry)
    if rotflags[2]=='#':
     lx.eval('item.channel rot.Z %s' % -rz)

対称のコントローラの方は互いに値を交換しなくちゃならないから位置も回転もまずxyzチャンネルの値を全て取得しておく。対称アイテムは2つで1セットなのでACS_Itemsに入っているアイテム全てを処理していくと2回同じセットを処理しちゃう事になるので「done」に処理が終わったアイテム名を記録しておいて「(not item in done)」の条件式で2回目の処理はしないようにしている。ちなみに登録する名前は相手側のアイテム名だけにしてあるよ。相手側の名前はループが進めば出てくるけど今処理している名前は2度と出てこないからね。検出するのは相手側の名前だけでいいわけだ。あとは「RGCH」のタグがあるものを見つけてそのアイテムを選択後に位置と回転のチャンネル値をそれぞれ取得する。

#For Symmetry Items
done=[]
for item in ACS_Items.keys():
 if (not item in done) and (ACS_Items[item][1] != None):
  id1 = ACS_Items[item][0]
  id2 = ACS_Items[item][1]
  pairname = lx.eval('query sceneservice item.name ? %s' % id2)
  done.append(pairname)
  tagtypes = lx.evalN('query sceneservice item.tagTypes ? %s' % id1)
  if "RGCH" in tagtypes:
   rgch = lx.eval('item.tag mode:string tag:"RGCH" value:?')

   lx.eval('select.item %s mode:set' % id1)

   px1=lx.eval('item.channel pos.X ?')
   py1=lx.eval('item.channel pos.Y ?')
   pz1=lx.eval('item.channel pos.Z ?')
   rx1=lx.eval('item.channel rot.X ?')
   ry1=lx.eval('item.channel rot.Y ?')
   rz1=lx.eval('item.channel rot.Z ?')

   lx.eval('select.item %s mode:set' % id2)

   px2=lx.eval('item.channel pos.X ?')
   py2=lx.eval('item.channel pos.Y ?')
   pz2=lx.eval('item.channel pos.Z ?')
   rx2=lx.eval('item.channel rot.X ?')
   ry2=lx.eval('item.channel rot.Y ?')
   rz2=lx.eval('item.channel rot.Z ?')

「item.channel」コマンドはチャンネルを選択してからアイテムを選択しないでチャンネル値を取得する方法と、アイテムを指定してチャンネルを取得する方法の2通りがあるけど今回はアイテムの選択をしてやっている。コマンドのパラメータを見ると

item.channel name:string ?value:* <mode:{set|shift|scale}> <item:&item>

となっているのでアイテムを選択しないで

px1=lx.eval('item.channel pos.X ? item:%s' % id1)

でもOKなはずだ。こうすればスクリプトの冒頭で選択アイテムを記憶して最後にその選択状態を復帰させる必要はなくなる。

両方の位置と回転の値が取得できたらあとは位置と回転のチャンネルのフラグを確認して有効(値が#)なものに反転した値をセットする。位置はXチャンネルだけ符号反転。回転はYZチャンネルの符号を反転してセットする。直前にid2のアイテムが選択されているのでそれにid1のアイテムの値をセットして、それからid1のアイテムを選択してid2の値を反転したものをセットする。

   ppos=rgch.find("p")
   if ppos!=-1:
    posflags=rgch[ppos+1:ppos+4]

    if posflags[0]=='#':
     lx.eval('item.channel pos.X %s' % -px1)
    if posflags[1]=='#':
     lx.eval('item.channel pos.Y %s' % py1)
    if posflags[2]=='#':
     lx.eval('item.channel pos.Z %s' % pz1)

   rpos=rgch.find("r")
   if rpos!=-1:
    rotflags=rgch[rpos+1:rpos+4]
    if rotflags[0]=='#':
     lx.eval('item.channel rot.X %s' % rx1)
    if rotflags[1]=='#':
     lx.eval('item.channel rot.Y %s' % -ry1)
    if rotflags[2]=='#':
     lx.eval('item.channel rot.Z %s' % -rz1)

   lx.eval('select.item %s mode:set' % id1)
   if ppos!=-1:
    posflags=rgch[ppos+1:ppos+4]

    if posflags[0]=='#':
     lx.eval('item.channel pos.X %s' % -px2)
    if posflags[1]=='#':
     lx.eval('item.channel pos.Y %s' % py2)
    if posflags[2]=='#':
     lx.eval('item.channel pos.Z %s' % pz2)

   if rpos!=-1:
    rotflags=rgch[rpos+1:rpos+4]
    if rotflags[0]=='#':
     lx.eval('item.channel rot.X %s' % rx2)
    if rotflags[1]=='#':
     lx.eval('item.channel rot.Y %s' % -ry2)
    if rotflags[2]=='#':
     lx.eval('item.channel rot.Z %s' % -rz2)

これで中心と左右のトランスフォーム用のコントローラの位置と回転はすべて反転出来た。

下のGIFアニメはこれを位置と回転のみ変更してポーズをつけたものに適用してみたもの。ポーズが左右反転するのがわかる。まだ他のコントローラの値は反転していないので、IK−FKバランスとかをいじったりしていると完全な反転は出来ない。

fig01

ここまでの部分はアップロードしておいたよ。

それではまた次回。

modo701ブログ目次



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG

2014年01月28日

2014の新機能を調べてみた その75 3dsmax 2014

前回は「Autodesk Point Cloud Material」について調べてみた。前回の方法はポイントクラウド周辺の環境をマテリアル自身がシェーディングしてそれを自己発光マテリアルとしてサーフェスに適用してレンダリングするような方法で、他から投影される影なんかも全てマテリアル自身が処理するような感じだった。だからレンダリング結果を直接調整できる良さがある一方で、マテリアルに無い特性については手の出しようが無かった。

「Point Cloud Object」をレンダリングする方法にはこの他にも「Autodesk Point Cloud Shader」を使う方法もある。

fig03

こっちの方法は「Arch&Design」や「標準」などのマテリアルに対してポイントクラウドから取得した色やポイントクラウドオブジェクト経由のランプシェーダーからの入力を使ってポイントクラウド表面のマップデータを提供する方式だ。だからこっちの方式ではオブジェクト表面の明るさや影などの処理はそれを適用するマテリアルの方でやってもらうことになる。

下のGIFアニメはライトの明るさを変化させた時の2つの方式の違いを比較したものだ。

こっちは前回登場の「Autodesk Point Cloud Material」を使ったもの。ポイントクラウドオブジェクトに落ちた影の濃さは変化してるけどライトが当たっている部分の明るさに変化は無い。

fig01

こっちは「Arch&Design」マテリアルの「拡散反射光カラーマップ」に「Autodesk Point Cloud Shader」を適用したもの。ライトが当たっている部分も影の部分もライトの明るさの変化に応じて変化している。そして「Arch&Design」マテリアルの「反射」と「光沢」のパラメータの設定に応じてポイントクラウドオブジェクトに光沢や映り込みも生じている。

fig02

特性にマッピングするわけだから当然反射カラーマップにマッピングすれば

fig05

反射カラーにもなったりする。

fig04

オブジェクト表面に生じているノイズのようなものはどうやらポイントに割り当てられたポリゴンが他のポリゴンに落す影のようだ。ポリゴンを大きくしてみるとその影響がわかりやすい。

fig07

だからライトのシャドーをOFFにするとザラザラは消える。

fig08

「Autodesk Point Cloud Shader」のパラメータは下の3つで、前回出てきた「Autodesk Point Cloud Material」と同じだ。

RampShaderについては必要な時には手動でポイントクラウドにマッピングされている「Ramp Shader」を接続する。

fig06

「Use Color Channel as」はポイントクラウドオブジェクトから受け取る情報を接続したランプシェーダーに渡して変換したものをどう出すかを決めるだけのもののようだ。出口だけしか決められないから取得するデータについてはポイントクラウドオブジェクト側で設定しないとならない。だから基本的にこの部分はポイントクラウドオブジェクトと一致させておかないと期待した動作にならない。例えば「高度ランプ」は1チャンネルのデータで「標準(法線)ランプ」はRGBの3チャンネルデータなので、これを双方に互い違いに設定しても表示がおかしくなるだけだ。

fig09

fig10

残念ながらこのランプシェーダーはポイントクラウドオブジェクトに対して1つしか設定出来ないので、ポイントクラウドシェーダーを複数作って1つは法線ランプに、もうひとつは高度ランプにといった使い方は出来ないようだ。

それではまた次回。

maxまとめページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)3ds Max | CG

2014年01月27日

ACSを使ってみた その46 modo701 SP2

ようやく左右で対になったコントローラと中心にあるコントローラの把握が出来たので今度はこれらを使ってコントローラを操作することを考えてみたい。

まず左右対称のコントローラの回転だけど、手首の左右のプラス方向の回転はこんな感じになっているようだ。これを見ると左右対称にしたければX軸の角度はそのままコピーでOKでYとZは符号を反転してコピーする必要があるようだ。

fig01

中心も同様で、XはそのままYとZは符号反転すれば左右反対のポーズになる。

fig02

逆に位置の方はXだけ符号反転でYとZはそのままでOKだ。

fig03

さらにリグには回転の順番を変えたりコントローラのウエイトを変えたりと言った機能があるのでそれらについても考えなくっちゃならないな。

まず手始めに中心部分のコントローラの回転を反転させる事をやってみたい。回転で注意しなくちゃならないのは角度をどう表現するかで、スクリプトからチャンネルをクエリしたりコマンドを発行したりすると「度」と「ラジアン」が混在する事がよくある。これを統一するためにまずオプションを設定する。

lx.option("queryAnglesAs","degrees")

これで角度を取得する時に「度」単位で取得出来るようになる。

また、回転させる必要があるコントローラは「RGCH」というタグがあって、その値の中に「r###」などの表記があることがカスタムリグを作る時にわかったのでこれを利用してみたい。

中心にあって左右のペアがないコントローラは「ACS_Items」の値のアイテムIDリストの2番目の要素がNoneになっているので、それを識別に使って、

for item in ACS_Items.keys():
 if ACS_Items[item][1] == None:

そのコントローラのアイテムIDからそのアイテムのタグタイプリストを取り出す。

id = ACS_Items[item][0]
tagtypes = lx.evalN('query sceneservice item.tagTypes ? %s' % id)

このタグタイプの中に「RGCH」があればトランスフォーム用のコントローラになる。Pythonではリストなどの中に特定の要素があるかどうかを調べる「in」と言う便利な演算子があるのでそれを使うとこんな感じで調べられる。

if "RGCH" in tagtypes:

タグタイプ「RGCH」が見つかったらこのタグの値を「item.tag」コマンドを使って取得する。そのためにまずこのアイテムを選択する。「modo:set」で選択すると他に選択しているアイテムを全解除してから指定のアイテムのみを選択しなおす。

lx.eval('select.item %s mode:set' % id)

そして「RGCH」タグの値を取得する。

rgch = lx.eval('item.tag mode:string tag:"RGCH" value:?')

読み取ったタグの値の中から「r 〜」となっている部分を探し出す。「String」クラスの「find」メソッドは指定した文字列が見つかればその位置を返すもので、見つからなければ−1を返す。

rpos=rgch.find("r")

見つかったら「r」の後ろの3文字を抜き出して調べる。この3文字がXYZの各チャンネルに対応していて、文字が「#」の場合は有効なチャンネルになっているようだ。Pythonでは文字列の一部分を「 [開始位置:終了位置] 」としてやることで抜き出す事が出来る。「r」が見つかった位置の次の文字から4文字目まで抜き出せば、「r」の後ろの3文字が抜き出せる。

if rpos!=-1:
 rotflags=rgch[rpos+1:rpos+4]

中心にあるコントローラの回転はXはそのまま、YとZの符号を反転すればいいので、「item.channel」コマンドでYとZの回転角度を取得する。この時取得できる値はオプションで設定したので「度」単位になる。

ry=lx.eval('item.channel rot.Y ?')
rz=lx.eval('item.channel rot.Z ?')

あとは各チャンネルにあたる文字が「#」の場合のみチャンネルの符号を反転して適用するだけだ。

if rotflags[1]=='#':
 lx.eval('item.channel rot.Y %s' % -ry)
if rotflags[2]=='#':
 lx.eval('item.channel rot.Z %s' % -rz)

これで中心部分の回転コントローラは反転出来るようになった。

fig04

ここまでの部分はアップロードしておいたよ。

それではまた次回。

modo701ブログ目次



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG
Archives