2014年04月

2014年04月28日

ゴールデンウィーク中は定期更新はお休みします

3dsmax2015にmodo801と今年も新作CGソフトのシーズンがやって来たね。

max2015はもう届いて、801も入手予定なんだけど、3Dプリンターも買っちゃったし(いろいろあってようやく稼動まで漕ぎ着けたのはほんの数日前と言う・・・orz)、5月と言えば農作物もいろいろ手をつけないと間に合わないしで、もうどれから手をつけていいのやら・・・。

fig01

と言うわけで今年もGW中はレギュラーの定期更新はお休みします。

3Dプリンターはさっそくmodoでモデリングして実用品(下の写真は3Dプリンターで作った3DS用画面撮りカメラマウンターw)作ったりしてるんだけど、

fig01

マシンの特徴とかもちゃんと把握しないとマズいなと思ったので現在テストを続行中。成果はちょっとずつ公開する予定だ。

それではみなさん楽しいGWを!



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)その他 

2014年04月25日

ACSを使ってみた その72 modo701 SP4

ACSリグのコントローラを網羅したラッパークラスがようやく出来た(あっベースコントローラ入れるの忘れた・・・)。

fig01

「ACS_Wrapper.py」ファイルを実行したいプログラムと同じフォルダーに置いて、このクラスをインポートすれば簡単にリグのパラメータがコントロール出来る。「Rig」クラスの初期化にはアクターのグループIDを与える。下の例では現在選択されているアクターのグループIDを渡して「Rig」クラスのインスタンスを生成し、各パラメータを変更している。実行結果が上の画像で、2つの別のリグに対して個別にこのプログラムを実行してみたところだ。

#python
import sys
import math
import lx
import ACS_Wrapper

def main():
 #search cntrol items from select actor
 actor = lx.eval1('group.current ? actr')

 #set ACS Pose
 rig = ACS_Wrapper.Rig(actor)

 rig.Body.Root.pos.Y.value = -0.1
 rig.Body.Root.rot.Y.value = 40
 rig.Body.Root.rot.Z.value = 15

 rig.LArm.Arm_CH.IK_Blending.value =  rig.RArm.Arm_CH.IK_Blending.value = 0
 rig.LArm.Pinky2.rot.X.value = rig.RArm.Pinky2.rot.X.value = 45
 rig.LArm.Hand_FK.rot.X.value = rig.RArm.Hand_FK.rot.X.value = -40
 rig.LArm.Hand_FK.rot.Y.value = -20
 rig.RArm.Hand_FK.rot.Y.value = 20
 rig.LArm.ForeArm_FK.rot.X.value = rig.RArm.ForeArm_FK.rot.X.value = 100
 rig.LArm.Arm_FK.rot.X.value = rig.RArm.Arm_FK.rot.X.value = 14
 rig.LArm.Arm_FK.rot.Y.value = 32
 rig.RArm.Arm_FK.rot.Y.value = -32
 rig.LArm.Arm_FK.rot.Z.value = 15
 rig.RArm.Arm_FK.rot.Z.value = -15

 rig.LLeg.Leg_CH.IK_Blending.value = rig.RLeg.Leg_CH.IK_Blending.value = 1
 rig.LLeg.Leg_IK_Pole.pos.X.value = 0.5
 rig.LLeg.Leg_IK_Pole.pos.Y.value = 0
 rig.LLeg.Leg_IK_Pole.pos.Z.value = -0.7
 rig.LLeg.Heel.pos.X.value = 0.0
 rig.LLeg.Heel.pos.Y.value = 0.2
 rig.LLeg.Heel.pos.Z.value = 0.0
 rig.LLeg.Heel.rot.X.value = 30
 rig.LLeg.Heel.rot.Y.value = 40
 rig.LLeg.Heel.rot.Z.value = 0

if __name__ == "__main__":
 main()

各パラメータは読み書きどちらも可能だ。

リグのコントローラは大きく下のグループに分けてあって、

Head
Body
LArm
RArm
LLeg
RLeg

この下に各コントローラが入っている。グループ分けしたのは左右のコントロールを簡単にしたいとか、各部分に追加の機能を持たせようと思っているからだ。例えば足ならどっかのアイテムにHeelをフィットさせたり、FKをIKにフィットさせたりと言ったところだ。この辺はこれからかな。

ダウンロードはこちら。

それではまた次回。

modo701ブログ目次

ゴールデンウィーク中は定期更新はお休みします。



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

2014年04月24日

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

前回に引き続き「demoAnimation.py」で使われている「Animation」クラスのメソッドについて調べてみたい。

今回調べたメソッドは主に「時間設定」ダイアログ内の設定に関するものだった。

fig01

次の2つのメソッドは「時間設定」の「再生」ロールアウトの「リアルタイム」の状態の取得と設定のためのものだ。「True」なら「ON」で、「False」なら「OFF」だ。

GetRealTimePlayback() -> bool
SetRealTimePlayback(bool realTime)

同様に次のメソッドは「アクティブビューポートのみ再生」の状態の取得と設定のためのものだ。

GetPlayActiveOnly() -> bool
SetPlayActiveOnly(bool playActive)

そしてこれらは「ループ」の状態の取得と設定で、

GetPlaybackLoop() -> bool
SetPlaybackLoop(bool loop)

これらは「速度」の状態の取得と設定だ。

GetPlaybackSpeed() -> int
SetPlaybackSpeed(int s)

スピードはsの値が正の場合はs倍になり、負の場合は1/s倍になる。4Xなら4で1/4Xなら−4だ。

「StartPlayback()」メソッドはシーンを再生する。「selOnly」が0の時はシーン内の全てのアニメーションを再生して、それ以外の時は選択したアイテムのみアニメーションを再生する。このメソッドは再生が終了するまで制御を戻さない。だからループがONになっている場合、手動で再生をストップさせないとスクリプトは先に進まない。

StartPlayback(int selOnly=0)

このメソッドは「再生」ボタンのフライアウトで表示される2つのボタンに該当する。

fig02

「EndPlayback()」は再生を止める。ただしスクリプト中で「StartPlayback()」を使ってシーン再生を始めた場合、制御が戻ってこないので、このメソッドで再生を止めることは出来ないようだ。シーンが再生中にスクリプトを起動した場合、このメソッドでその再生を止める事が出来る。

EndPlayback()

同様に「IsPlaying()」は現在シーンが再生中かを調べるものだけど、これも上記と同様の理由で、使えるのは手動でシーンを再生したり止めたりしている時だけだ。

IsPlaying() -> bool

以上が「Animation」クラスの全メソッドだ。

それではまた次回。

maxまとめページ



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

2014年04月23日

コマンドとクエリ上でのワールド座標系とローカル座標系について modo701 SP4

ACSのリグに限らずアイテムはいろいろペアレントされるから、ペアレントしている親座標系で表わされる変換チャンネルの座標値だけではそれがシーン上のどこにいてどっちを向いているのか全くわからない。401まではその位置のままペアレントを解除して座標値や回転角度を取得するとかするのが手っ取り早かったんだけど、501からはアイテムの位置や回転の座標系が親座標とワールド座標の両方で出来るようになっていたようだ。たまにちゃんと調べないと効率の悪いことしちゃってるよねw

これがそのコマンド。「mode」パラメータで「local」と「world」が切り替えられる。

item.setPosition <x:distance> <y:distance> <z:distance> <mode:{local|world}>
item.setRotation <x:angle> <y:angle> <z:angle> <mode:{local|world}>

アイテムの位置や回転角度を調べる場合も、

query sceneservice item.pos ? アイテムID
query sceneservice item.rot ? アイテムID

の場合は親座標系の位置と回転角度で、

query sceneservice item.worldpos ? アイテムID
query sceneservice item.worldrot ? アイテムID

としてやれば、ワールド座標系での位置と回転が得られる。

ただ、ワールド座標での回転については単純な角度じゃなくて3X3の変換行列になる。これがどんなもんなのか調べてみたらアイテムとペアレントしている親アイテムなどの回転変換を相殺する変換行列になっていた。つまりこの変換を使うとアイテムの回転角度を[0,0,0]にした上でペアレントを解除した時と同じ位置に頂点が移動する(ただし移動は含まない)。

例えば下の画像のように3つのアイテムをペアレントした場合、

fig01

mesh019の各頂点のローカル座標値に対して「item.worldpos」クエリで得られたワールド座標系の位置を差し引いてから「item.worldrot」クエリで得られた変換行列で変換すると、mesh019をペアレントから外して、アイテム座標を[0,0,0]、回転を[0,0,0]にした場合と同じ位置にメッシュが移動する。

#python
import sys
import math
import lx

def mmul(M,v):
 x = M[0]*v[0]+M[1]*v[1]+M[2]*v[2]
 y = M[3]*v[0]+M[4]*v[1]+M[5]*v[2]
 z = M[6]*v[0]+M[7]*v[1]+M[8]*v[2]
 return [x,y,z]

def vsub(v1,v2):
 x = v1[0]-v2[0]
 y = v1[1]-v2[1]
 z = v1[2]-v2[2]
 return [x,y,z]

M = lx.eval('query sceneservice item.worldRot ? mesh019')
iwpos = lx.eval('query sceneservice item.worldPos ? mesh019')
lx.eval('select.type item')
lx.eval('select.item mesh019')
lx.eval('select.type vertex')

main = lx.eval('query layerservice layer.index ? main')
verts = lx.evalN('query layerservice verts ? all')

for v in verts:
 pos = lx.eval('query layerservice vert.pos ? %s' % v)
 wpos = mmul(M,vsub(pos,iwpos))
 lx.eval('select.element %s vertex set %s' % (main,v))
 lx.eval('vert.set x %s' % wpos[0])
 lx.eval('vert.set y %s' % wpos[1])
 lx.eval('vert.set z %s' % wpos[2])

これが変換後。「mesh019」のアイテムはペアレントしたままで座標値や回転角度もそのままだ。

fig02

このように「item.worldrot」クエリで得られる変換行列はペアレントやアイテム回転による変換をキャンセルするように働く。

だから当然逆行列をとれば、その逆も可能だ。下の画像はペアレントされていないアイテム「mesh123」を追加したところ。これはワールド座標系原点に回転角度[0,0,0]で配置されている。

fig03

このアイテムの各頂点に対して「mwsh019」からクエリした回転行列の逆行列で回転させてから「mwsh019」からクエリしたアイテムのワールド座標値を加算するプログラムが下記だ。

#python
import sys
import math
import lx

def det33(M):
 return M[0]*M[4]*M[8]+M[3]*M[7]*M[2]+M[6]*M[1]*M[5]-M[0]*M[7]*M[5]-M[6]*M[4]*M[2]-M[3]*M[1]*M[8]

def invM33(M):
 invM = []
 det = det33(M)
 if det==0.0:return None
 invM.append((M[4]*M[8]-M[5]*M[7])/det)
 invM.append((M[2]*M[7]-M[1]*M[8])/det)
 invM.append((M[1]*M[5]-M[2]*M[4])/det)
 invM.append((M[5]*M[6]-M[3]*M[8])/det)
 invM.append((M[0]*M[8]-M[2]*M[6])/det)
 invM.append((M[2]*M[3]-M[0]*M[5])/det)
 invM.append((M[3]*M[7]-M[4]*M[6])/det)
 invM.append((M[1]*M[6]-M[0]*M[7])/det)
 invM.append((M[0]*M[4]-M[1]*M[3])/det)
 return invM

def mmul(M,v):
 x = M[0]*v[0]+M[1]*v[1]+M[2]*v[2]
 y = M[3]*v[0]+M[4]*v[1]+M[5]*v[2]
 z = M[6]*v[0]+M[7]*v[1]+M[8]*v[2]
 return [x,y,z]

def vadd(v1,v2):
 x = v1[0]+v2[0]
 y = v1[1]+v2[1]
 z = v1[2]+v2[2]
 return [x,y,z]

def vsub(v1,v2):
 x = v1[0]-v2[0]
 y = v1[1]-v2[1]
 z = v1[2]-v2[2]
 return [x,y,z]

M = lx.eval('query sceneservice item.worldRot ? mesh019')
iwpos = lx.eval('query sceneservice item.worldPos ? mesh019')
invM = invM33(M)
lx.eval('select.type item')
lx.eval('select.item mesh123')
lx.eval('select.type vertex')

main = lx.eval('query layerservice layer.index ? main')
verts = lx.evalN('query layerservice verts ? all')

for v in verts:
 pos = lx.eval('query layerservice vert.pos ? %s' % v)
 wpos = vadd(mmul(invM,pos),iwpos)
 lx.eval('select.element %s vertex set %s' % (main,v))
 lx.eval('vert.set x %s' % wpos[0])
 lx.eval('vert.set y %s' % wpos[1])
 lx.eval('vert.set z %s' % wpos[2])

そしてこれが実行結果。mesh019とmesh123が重なっているのがわかる。

fig04

以上のように「item.worldrot」で得られる9つの値が回転行列だってのはわかったけど、「item.rot」で3つの回転角度が得られるのに対してこれは回転行列というのはちょっと使いにくいね。もしアイテムのワールド回転角度が欲しかったら「その場でアンペアレント」でペアレントを解除してから「item.rot」でクエリし、また「その場でペアレント」で戻すとかしちゃうのが手っ取り早いのかな。

それではまた次回。

modo701ブログ目次



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

2014年04月22日

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

前回に引き続き「demoAnimation.py」で使われている「Animation」クラスのメソッドについて調べてみたい。

「カスタマイズ」の「基本設定」パネルの「アニメーション」タブ内の設定についても「Animation」クラスからアクセス出来るようだ。

fig01

次の2つのメソッドは「オートキー既定フレーム」のチェックボックスの状態を調べたり変更したりするためのものだ。

GetAutoKeyDefaultKeyOn() -> bool
SetAutoKeyDefaultKeyOn(bool setKey)

fig02

これがONの時はキーの無いチャンネルにオートキーでキーイングすると0か1フレームにも必ずキーが打たれるようになる。0になるか1になるかはその隣のパラメータで決める。その値を調べたり決めたりするのが次の2つのメソッドだ。

GetAutoKeyDefaultKeyTime() -> TimeValue
SetAutoKeyDefaultKeyTime(TimeValue t)

ここで有効な設定は0フレームか1フレームなので、「TimeValue t」の値はそれをTick単位に直したものになる。例えば30FPSなら0か160って事だ。

次の2つのメソッドは「既定値のパラメトリックコントローラ範囲を無効にする」チェックボックスの状態の取得と変更のためのものだ。

GetControllerOverrideRangeDefault() -> bool
SetControllerOverrideRangeDefault(bool override)

fig03

次の3つのメソッドはベジェコントローラでキーが作成される時のイン側とアウト側の接線タイプを取得、設定するためのものだ。取得の方は「イン」側と「アウト」側で別れてるけど、設定の方は1つのメソッドで行うようになっている。またその設定をコンフィグファイルに書き出すかどうかのパラメータもある。

GetDefaultInTangentType() -> int
GetDefaultOutTangentType() -> int
SetDefaultTangentType(int dfltInTangentType, int defaultOutTangentType, bool writeInCfgFile=True)

既定値についてはオートキーの横に設定メニューがあり、このメニューからは「イン」と「アウト」が同じタイプの接線既定値が設定できる。

fig04

両側で変更したい場合は、「カスタマイズ」の「基本設定」パネルの「アニメーション」タブ内の「コントローラ既定値」の「既定値を設定」から

fig05

ベジェ系のコントローラを選んで「設定」ボタンを押せば、

fig06

設定パネルが出てくる。

fig07

ここで選択出来る接線タイプは7種類あって、「SetDefaultTangentType」のパラメータで0〜6の番号が割り当てられている。ベジェキーの既定値でもボタンを押すとフライアウトが展開される。上から「スムーズ」「線形」「ステップ」「高速」「低速」「スプライン」「自動接線」だ。

fig08

これを番号に直すと、

スムーズ:0
線形:1
ステップ:2
高速:3
低速:4
スプライン:5
自動接線:6

になる。「GetDefaultInTangentType()」や「GetDefaultOutTangentType()」で既定値を読めば該当するタイプの数値が返って来るし、「SetDefaultTangentType」でこれらの数値を指定すればそのタイプが既定値の接線タイプになる。

それではまた次回。

maxまとめページ



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