2018年12月16日日曜日

[houdini] node作成時に独自パラメーターを追加する方法

ノード生成時に独自のパラメーターを追加する方法を説明します。
(かれこれ5、6年前のh11とかh12の頃にやってた事なので、
 既知の内容かもしれませんが。。。)

例として、
ropのgeometryノードにパラメーターを追加します。

ここでは、geometry ropが元々持っているパラメーターを、
「Default」と言うフォルダ(タブ)を作ってそちらに全部入れ、
その上で新たに「Backburner」と言う名前のタブを追加してます。

これはデフォルトのパラメーター、    
 

こちらが新たに追加したタブとパラメーター

















どうやるか?

Houdiniには、
ノードが作成された直後、用意したスクリプトを実行してくれる仕組みがあります。

・OnCreated.py(どんなノード生成時も実行される)
・ノードタイプ名.py(ノードタイプ名のタイプのノードが作成された時だけ実行)
・ノードタイプ名_oncreated.py(HDAの場合)

上記スクリプトの中でパラメーターを追加するコードを書いておけば、
ノード生成時に独自のパラメーターを追加しておく事が可能です。


・OnCreated.py、ノードタイプ名_oncreated.pyは、
HOUDINI_PATHに通したパスの中の、/scripts/pythonに
ファイルを置いておけば実行してくれます。

・ノードタイプ名.pyは、
ノードはカテゴリ分けされており、
HOUDINI_PATHに通したパスの中の、/scripts/カテゴリ名フォルダ
にファイルを置いておけばいいです。


パラメーターの作り方

pythonでパラメーターを作成、追加することは可能ですが、
手動でコード書くのはかなりめんどくさい・・・

そこで、houdini上のUIでパラメーターを作成し、
それをpythonコードとして書き出す事ができるので、
書き出しておいて、後はそれを実行することで追加していきます。

パラメーターの追加は以下「Edit Parameter Interface」








「Backburner」以下追加してます。
















houdiniのパラメーターの持ち方

houdiniは、個々のノードが、parmTemplateGroupと言う入れ物を持っており、
この中にパラメーターとなるオブジェクトを持ってます。

各パラメーターは、
parmTemplateと言うクラスから派生したオブジェクトとなっており、
parmTemplateGroupが、それを持っています。

parmTemplate class
http://www.sidefx.com/docs/houdini/hom/hou/ParmTemplate.html

parmTemplateGroup class
http://www.sidefx.com/docs/houdini/hom/hou/ParmTemplateGroup.html




Pythonでノードにパラメーターを追加する方法

以下でまさにサンプルコードが載ってます。
http://www.sidefx.com/docs/houdini/hom/hou/Node.html#parmTemplateGroup

順を追って説明すると、

# ノードを作成(/obj以下にgeometryノードを作成)
node = hou.node("/obj").createNode("geo")

# ノードが持つparmTemplateGroup()(箱)を取得
group = node.parmTemplateGroup()

# My Parmsと言うラベルの付いたフォルダを作成
(フォルダはパラメーターのタブに当たります)
folder = hou.FolderParmTemplate("folder", "My Parms")

# フォルダにfloat型の myparm という名前のパラメーターを追加
folder.addParmTemplate(hou.FloatParmTemplate("myparm", "My Parm", 1))

# 最初に取得したparmTemplateGroup(箱)にフォルダごと追加
group.append(folder)


で、最後がポイントなのですが、
上記、group.append(folder) で、
parmTemplateGroup()で取得した箱にフォルダを追加しているのですが、
この時点ではノードにパラメーターは追加されず、何の変化もありません。

このparmTemplateGroup()で取得した箱と言うのは、
あくまでコピーを取得しており、
もはや実際のノードがもつ箱ではないので、
これに何を追加してもノードには反映されません。

ではどうするか、
最後に以下を行うことで追加したパラメーターを持つ箱parmTemplateGroupをセットでき、
めでたくパラメーターを追加することが可能になります。
node.setParmTemplateGroup(group)

2012年6月3日日曜日

Maya CardStickの解説2

さて、前回「Maya CardStickの解説1」の続きで、

Particleがオブジェクトに衝突して、 衝突したオブジェクトにくっつく件です。


 大雑把な流れとしては、
if( コリジョンした時 )
{
 // cube1のInverse Matrixを取得
 float $invm[] =`getAttr cube1.worldInverseMatrix`;

 // collisionした時のParticleの位置取得
 vector $p = position;

 // $pにmatrix $invmを掛け算
 vector $newP = multMat( $p, $invm );

 // この時の値を保存
 collisionPosPP = $newP;
}
else if( コリジョンした以降のフレームでは )
{
 // cube1のMatrixを取得
 float $m[] =`getAttr cube1.worldMatrix`;

 // 保存した値を取り出し
 vector $p = collisionPosPP;

 // $pにmatrix $mを掛け算
 vector $newP = multMat( $p, $m );

 // 値を入れる
 position = $newP;
}
と言う感じでしょうか。 めんどくさくなって来たので、とりあえずコードを載せます。
以下はRuntime after dynamicsのExpressionです。
// コリジョンした場合に0から始まる数字が入ります。
// コリジョンしていない時は-1が入ります。
int $gid = particleShape1.collisionGeometryIndex;

// コリジョンした時
if( $gid!=-1 )
{
 // get collision shape
 string $geo = getCollisionShape($gid);

 // get inverse Matrix of collision shape
 float $m[] = `getAttr ($geo+".worldInverseMatrix")`;

 // multiply $p * $m
 vector $p = particleShape1.position;
 $p = multMat( $p, $m );

 // set attribute
 particleShape1.collideGeoId = $gid;
 particleShape1.collisionPosPP = $p;
 particleShape1.traceDepthPP = 0;

}
// コリジョンした以降のフレーム
else if( particleShape1.collideGeoId!=-1 )
{
 // get collision shape
 $gid = particleShape1.collideGeoId;
 $geo = getCollisionShape($gid);

 // get Matrix of collision shape
 float $m[] = `getAttr ($geo+".worldMatrix")`;


 // multiply $p * $m
 vector $p = particleShape1.collisionPosPP;
 $p = multMat( $p, $m );

 particleShape1.position = $p;

}
float型のPar Particle AttributecollideGeoId
vector型のPar Particle AttributecollisionPosPPを追加しておいてください。

まず、 最初のif文、コリジョンしたかどうかは
collisionGeometryIndex
と言うAttributeを見ると、-1ではない値が入るので分かります。

次に9行目
getCollisionShape();
と言う関数でコリジョンしたShape名を取得するようにしています。
これは、コリジョンしたオブジェクトのshape名を取得する関数で、 以下を作成しました。
この関数定義も Runtime after Expressionの最初の方にでも書いておきましょう。
proc string getCollisionShape( int $gid )
{
 string $gshape;
 string $aGeoConnect[] = `listConnections particleShape1.collisionGeometry`;
 string $aGeo[] = `listConnections -s 1 -d 0 -p 1 ($aGeoConnect[$gid]+".localGeometry")`;
 string $aAttr[] = stringToStringArray($aGeo[0], ".");
 $gshape = $aAttr[0];

 return $gshape;
} 
何をやっているのかと言うと、
Particleにコリジョンするオブジェクトは「Make Collide」でコリジョン設定していると思いますが、
その際、 オブジェクトのShapeは、Particleのアトリビュート「collisionGeometry」にコネクションされます。
そして、 「collisionGeometryIndex」で得られる番号が 「collisionGeometry」に接続されている配列番号に対応しており、
実際には、「particle <- geoConnector <- mesh 」と言うノード構成で接続されているので、
それをたどってShape名を取得するようにしています。

次に、16行目の以下の関数ですが、
$p = multMat( $p, $m );
これは、 引数vector型$pにmatrix配列$mを掛け算してvector値を返す関数で、

melコマンドとして以下があるのですが、
やけに処理が遅い上に、結果が変でした・・・
float[] pointMatrixMult ( float $point[], float $matrix[] )
なので、melで組んでます。
この関数も Runtime after Expressionの最初にでも書いておきましょう。


proc vector multMat( vector $p, float $mat[] )
{
 float $fp[4] = {$p.x,$p.y,$p.z,1};
 float $result[4];
 for( $i=0; $i<4; $i++ ){
  $result[$i] = 0.0;
  for( $j=0; $j<4; $j++ ){
     $result[$i] += $fp[$j] * $mat[$i+$j*4];
  }
 }
 return <<$result[0],$result[1],$result[2]>>;
}
そんで、
collideGeoIdにcollisionGeometryIndex, collisionPosPPにinverseしたpositionを保存し、
21行目で
traceDepthPP = 0;

これは、コリジョン精度になりますが、0にする事でそれ以降コリジョンしなくなります。
で、これをやっておかないと、「collisionGeometryIndex」が常に-1以外になってしまい、
いつでもコリジョンしている状態になるので、これをやります。

コリジョン以降の処理については、
なんとなく分かるかと思うので説明は省略します。


最後に、
コリジョンオブジェクトについている、「geoConnector」ノードの
「Friction」は1にしておいてください。

1以下だと、Particleがコリジョン時に一瞬滑ります。
何でしょうかねぇ・・・
コリジョン時にExpressionの計算が聞いてない感じで・・・

では次回は変形しているオブジェクトへの対応についてか、
Rolling Ballについてか、その辺を書こうかなと。

2012年5月24日木曜日

Maya CardStickの解説1


以前投稿した、「Maya CardStick」の解説を書いてみようと思います。


ムービーでは、Deformしているオブジェクトにも対応しているのですが、
話しが長くなるので、とりあえずは、Deformなしバージョンの解説から。


まず、カードと言うか飛んでる板状のものはParticle Instancerで作成してます。
Particleは飛んでいってコリジョンした後、
コリジョンしたオブジェクトにくっつくわけですが、


まずは、

「コリジョンしたオブジェクトにくっつく(変形しないオブジェクト篇)

と言う事をどうやってるか、からお話しようと思います。




■コリジョンの情報について


どうやったか、の前に

ParticleのAttributeにはコリジョンした際の情報が入るものがいくつかあり、

collisionWorldPosition:コリジョンしたWorld位置
collisionGeometryIndex:コリジョンオブジェクトごとに割り振られたID
collisionComponentId:衝突したFace番号
collideFaceU:
衝突した位置のU座標
collideFaceV:
衝突したした位置のV座標

等、情報が取得できます。

これらは、すべてParticleの粒毎のPer Particle Attributeで、


例えば、衝突したかどうかだけ知りたい場合は、
collisionGeometryIndex
を見ると、

衝突時に、0以上の値が入り、それ以外のフレームでは -1 が入るので、判定が可能です。




で、本題ですが、

「衝突したオブジェクトにくっ付ける」 と言うのを言い換えて
          ↓
「一粒一粒を、衝突した時にコリジョンしたオブジェクトの子供にする(親子付けする)」
(衝突後はダイナミクス無視で)


方法でやりました。


そんな事ができるのか?
いや、標準にはそんな機能はないので・・・

親子付けを自分で計算します・・・




では、親子付けをどうするか、ですが、

衝突した時のParticleの位置は、
その時の、collisionWorldPositionに入っているので分かります、


そのcollisionWorldPosition
衝突したオブジェクトのInvert Matrixを掛け算します。
衝突したオブジェクトを仮にcube1として、仮のExpressionを書くと、



float $im[] =`getAttr cube1.worldInverseMatrix`;  // cube1のInverse Matrixを取得
vector $p = collisionWorldPosition;           // collisionした時のParticleのWorld位置取得

vector $newP = multMat( $p, $m );    // $pにmatrix $imを掛け算

するとどうなるかと言うと、
Particleの粒を衝突したオブジェクトの子供にして、
衝突したオブジェクトのTranslate, Rotateはすべて0で、Scaleは1の状態

にもって行ったのと同じ状態が作れます。


左側が実際にcube1に赤いParticleが衝突した瞬間のフレームで、 上のTranslate,Rotate,Scaleがその時のcube1の状態です。

右側が、左のcube1とParticleにcube1のworldInverseMatrix を掛け算した状態で、
cube1とParticleの相対的な位置関係が変わってないことがなんとなく分かると思います。


cube1は右の状態に今度はworldMatrixを掛けると左の状態に行けます。Particleも同様に右の状態にcube1のworldMatrixを掛けると左の状態に持っていけます。

で、 
上記で計算したParticleの位置情報をCollision発生時に計算して保持しておきます。

衝突後のフレームでは、衝突したオブジェクトcube1
のworldMatrixを
先ほど保存した位置情報に掛け算します。


すると、
実質Particleを衝突したオブジェクトの子供に出来る。

つまり、くっつけることができた。
と言う流れにすることができるのです。




解説が難しいですなぁ・・・

次回は、
実際のExpressionの説明を書いてみようかと。

カードの向きと、Deformしているオブジェクトにくっ付ける方法は
次の次の、またいつかで。




2012年3月18日日曜日

[windows]右クリックメニューに「パスのコピー」追加

色んなソフトでファイルを開く際、
Windowsのエクスプローラーで開いているパスを
コピー&ペーストして目的のディレクトリまで行く
と言うことが多々あります。

プログラムを書いている際も、
テスト的にフルパスをコードに直接書く事もよくあり、
この場合、Windowsの「\」文字区切りだとまずい場合がほとんどで、
「\\」と二重にするか、Unixでの「/」に書き換えたりする必要があり、
とにかくめんどくさい。

と言うことで、
Windowsのエクスプローラーでファイルを選択して右クリックして、
以下のようなパスをコピーするメニューを追加しました。
















やることは2つ
1.メニューを追加
2.メニューが選択された際に実行されるプログラムを用意


1.メニューを追加

Windowsの右クリックメニューはレジストリで管理されているので
レジストリに追加する必要があります。
レジストリエディタで追加する場合は、
スタートメニューを起動し、「regedit」と打ち込んでレジストリエディタを起動。

「HKEY_CLASSES_ROOT\*\shell」まで行き

1.「shell」を選択して「右クリック->新規->キー」で、キーを追加(名前は何でもいい)














2.追加したキーの中にある「(規定)」と言うのをダブルクリックし、
出てきたダイアログの「値のデータ」に「UNCパスコピー」と入れてOK
これが右クリックした際に出てくるメニュー名になります。











3.1で追加したキーを右クリックし、1と同じ事をして新たにキーを追加。
ここで追加したキーの名前は「command」にしてください。

追加したキーの中にある「(規定)」と言うのをダブルクリックし、
出てきたダイアログの「値のデータ」に実行するコマンドを書きます。











今回、Pythonで書いたプログラムが実行されるようにしており、
「python "J:/python/winPathCopy.py" %1 UNC -dir」
と言うコマンドを入力。

「winPathCopy.py」は、パスをクリップボードにコピーするPythonScriptです。
「%1」には、エクスプローラーで選択しているファイルかディレクトリのフルパスがきます。

その後ろの、「UNC」や「-dir」は「winPathCopy.py」で用意したてきとうなフラグです。



■winPathCopy.pyの中身
# -*- coding: shift-jis -*-
import sys
import os.path
#***********************************************************
## clipboadへコピー
#***********************************************************
def copyToClipboad( msStr ):
    import subprocess
    try:
        p = subprocess.Popen(['clip'], stdin=subprocess.PIPE)
        p.stdin.write(msStr )
        p.stdin.close()
        retcode = p.wait()
        return True
    except OSError:
        return False
    except ValueError:
        return False
    return True

if __name__=='__main__':
    if len(sys.argv)==3:
        sys.exit()

    file_path = sys.argv[1]
    mode = sys.argv[2]

    if mode=='UNIX':
        file_path = file_path.replace('\\','/')
        path = file_path

    if len(sys.argv)==4:
        if sys.argv[3]=='-dir':
            path = os.path.dirname(file_path)

copyToClipboad(path)



■レジストリへの登録がめんどくさい

いちいち手でレジストリに登録するのも面倒なので
reg ファイルを用意しておくと、色んなマシンで実行して使えますと。

Windows Registry Editor Version 5.00
;==============================================;
;UNCパスコピー作成
;==============================================
[HKEY_CLASSES_ROOT\*\shell\unc_path]

[HKEY_CLASSES_ROOT\*\shell\unc_path]
@="UNCパスコピー"

[HKEY_CLASSES_ROOT\*\shell\unc_path\command]

[HKEY_CLASSES_ROOT\*\shell\unc_path\command]
@="python \"J:/python/winPathCopy.py\" %1 UNC -dir"
;==============================================
; UNCファイルパスコピー作成
;==============================================

[HKEY_CLASSES_ROOT\*\shell\unc_file_path]

[HKEY_CLASSES_ROOT\*\shell\unc_file_path]
@="UNCファイルパスコピー"

[HKEY_CLASSES_ROOT\*\shell\unc_file_path\command]

[HKEY_CLASSES_ROOT\*\shell\unc_file_path\command]
@="python \"J:/python/winPathCopy.py\" %1 UNC"
;==============================================
; UNIXパスコピー作成
;==============================================

[HKEY_CLASSES_ROOT\*\shell\unix_path]

[HKEY_CLASSES_ROOT\*\shell\unix_path]
@="UNIXパスコピー"

[HKEY_CLASSES_ROOT\*\shell\unix_path\command]

[HKEY_CLASSES_ROOT\*\shell\unix_path\command]
@="python \"J:/python/winPathCopy.py\" %1 UNIX -dir"
;==============================================
; UNIXファイルパスコピー作成
;==============================================

[HKEY_CLASSES_ROOT\*\shell\unix_file_path]

[HKEY_CLASSES_ROOT\*\shell\unix_file_path]
@="UNIXファイルパスコピー"

[HKEY_CLASSES_ROOT\*\shell\unix_file_path\command]

[HKEY_CLASSES_ROOT\*\shell\unix_file_path\command]
@="python \"J:/python/winPathCopy.py\" %1 UNIX"

2011年9月29日木曜日

Maya Rolling Ball



Houdini Automatic BallRolling System
をMayaで。

Ball は Sphere(mesh)を Particle Instancerでインスタンス。
Ball どうしはnParticleのSelf Collisionで、
回転はVelocityの方向に、進んだ分だけ回転するExpression書いてます。

nParticleの「Compute Rotation」でというのもありますが、
コリジョンしないと回転を計算してくれないので。

しかし、やっぱりMayaだと書かないとできない・・・
Houdiniでやりたいなあ。
できないけど・・・

Maya CardStick



Houdini CardStick System


を、Mayaでやってみました。

ExpressionでMatrixの掛け算だの色々書かないと出来ず・・・
いちいち情報取得や計算する為のコマンドなりノードなりがなく・・・
プラグイン書けばいいのですが、まあ、めんどくさい・・・

2011年9月26日月曜日

Houdini Tutorial

Houdini Tutorial Movie

http://vimeo.com/channels/54102

https://sites.google.com/site/pqhoudinitutorial/

始動

始動します。
ものすごく不定期ですが、
ちょこちょこ書いていこうかなぁと