見出し画像

Houdini Rigging Tips

蒸留スイさん主催「Houdini Advent Calendar 2023」に初参加させて頂きます!

今回はHoudini上でRigを作成するにあたって役立ちそうなTipsをいくつか紹介させて頂こうと思います。
サンプルファイルもありますので、そちらを確認しながらこの記事を読み進めて頂ければと思います。

サンプルファイルはこちら↓

1.MapPointsを使用したジョイントリネーム

KineFXの利点としては元のジョイント構造からプロシージャルにRigを組んでいくことが出来るのは言わずもがなですが、ジョイント構造の違うキャラクターに同じRig構造を適用したい場合、ジョイント名を簡単に直感的に変える方法として、MapPointsノードを使用する方法を考えました。
Nameノードで変えていってもよいのですが、例えば腰や首、膝等共通した骨構造であればジョイントの位置は大きく変わらない為、リネームしたい各ジョイントを直接関連付けていった方が分かりやすいです。

下の画像に「skeleton_origin」「skeleton_ex」があります。originが元のジョイント、exが追加で外部から持ってきたキャラクタージョイントと仮定します。

MapPointsノードを使用して、ex側のジョイントを選択後にドラッグ、origin側のジョイントに繋ぐように引っ張ると関連付けられます。例えば、ex側「koshi」ジョイント→origin側「waist」ジョイントを繋いでいきます。

「RenameMapPoint」にて実際にリネームを行っています。

記述したスクリプトの解説です。

int maxP = chi("op:../mappoints1/mappings");
string fromPass = "op:../mappoints1/from";
string toPass = "op:../mappoints1/to";
string from[];
string to[];
string splitFrom[];
string splitTo[];


//mappointを使用したリネーム
//------------------------------------------------------
for(int i=0; i<maxP; i++){
    splitFrom = split(ch(fromPass + itoa(i)), "=");
    from[i] = splitFrom[1];
    i@ptnum = nametopoint(0, from[i]);
}

for(int i=0; i<maxP; i++){
    splitTo = split(ch(toPass + itoa(i)), "=");
    to[i] = splitTo[1];
}

for(int i=0; i<maxP; i++){    
    setpointattrib(0, "name", nametopoint(0, from[i]), to[i], "set");
}

1行目はMapPointsノードにて関連付けたジョイントの総数を取得。
2行目は変更したいジョイント側(skeleton_ex)のパス指定用文字列。
3行目は参照するジョイント側(skeleton_origin)のパス指定用文字列。
4~7行目は必要な文字列情報取得に使用する配列。

変更したいジョイント用として、
14行目のforでジョイント総数分を回し、
15行目、chで変更したいジョイントを指定。「@name=shiri」等の文字列で取得されるのでそれをsplitで「=」の箇所で区切り、16行目で後ろ側を指定(splitFrom[1])し、それをfrom[i]に格納。格納された文字列はこの例では「shiri」。

19行目、上記と同様、参照するジョイント用として、上記と同じような処理を実行。

25行目でnametopointでジョイント名からポイント番号を取得、setpointattribで名前をセット。

といった手順を行っております。
これでリネームは完了です。

2.ジョイントスケールだけを変更

HoudiniからゲームエンジンにFBX等でデータを持っていく際、ジョイントスケールがずれる事が気になりました。
Maya上では長さの単位としてm、cmをFBX書き出し時に切り替えることができます。
Houdiniでは以下のページ下部に記載のある「Convert Units」にて単位を変更することで、インポート時のモデル表示スケールを変更することができます。
https://www.sidefx.com/ja/docs/houdini/ref/windows/import_fbx.html

ただ、この方法だとインポート時にしか調整できませんので、リグ作成時にジョイントスケールを都度変換できた方が何かと都合がよいです。

単純にジョイントスケールを大きくすると当然ながらジョイントの位置も大きくなった分広がっていきます。
今回はジョイント位置を変えずにジョイントスケール値だけを変更したいので、ジョイント位置を一旦原点値のrootにParent化を行い、その後ジョイントスケールを変更、再度元のジョイント構造に戻す、といった処理を行いました。

「ParentRootpoint」で以下の記述を行います。

#include <kinefx_hierarchy.h>

int parentP;

//parent化
//------------------------------------------------------
parentP = getparent(0, @ptnum);
setparent(0, @ptnum, nametopoint(0, "root"));

setpointattrib(0, "parentP", @ptnum, parentP, "set");

Parent化に関してはParentJointsノードがあるのでそれで対応は出来るのですが、これだと全て手作業で一つ一つの親子構造を繋ぎ直さないといけないので、今回はスクリプトで一括で行います。

先ず、1行目でkinefx専用のライブラリを読み込みます。これを読み込まないと後に続くParent化用の関数が実行できません。
7行目、getparentでポイントの親を取得。
8行目、setparentでrootジョイントに全てのジョイントをParent化。
10行目のsetpointattribで、parentPという名前でアトリビュートを追加し、そこにgetparentで取得したポイント番号を格納します。元の親子関係を残しておくことで再度元のジョイント構造にParent化することができます。

続いて、RigPoseノードを使用して実際にジョイントのスケールを変換します。

グループ名を

!@name=root

としておくことでroot以外のジョイント全てにスケール変更を行えます。

続く「SetParent」でparentPに残しておいた親ジョイントにParent化を行い、元のジョイント構造に変換します。

#include <kinefx_hierarchy.h>

int parentP;
parentP = point(0, "parentP", @ptnum);

setparent(0, @ptnum, parentP);

これでジョイントスケール変換は完了です。

3.ジョイント回転値を参照元ジョイントから一括変換

skeleton_ex側のジョイントの回転値がskeleton_originと違う場合、origin側情報を元にRigを組んでいますので、ex側から適用した際に正しく動作しなくなってしまいます。
そこで参照元であるorigin側の回転値をex側に一括変換します。

RigAttributeVOPノード「OrientJoints」内で、ForEachノードを使用します。

「OrientJoints」の1番目のインプットは「skeleton_ex」、2番目のインプットは「skeleton_origin」を適用。
GetPointTransformsノードで、1番目のインプットに繋いだポイントを取得、ForEach beginのelement outから、GetPointTransformノードで1番目と2番目のポイントをそれぞれ取得。

2番目インプット側のGetPointTransformノードのxformを、Matrix to Vector4ノードに繋ぎ、回転値(row1~row3)をVector4 to Matrixノードに、
1番目インプット側のGetPointTransformノードのxformを同じくMatrix to Vector4の移動値(row4)をVector4 to Matrixノードにそれぞれ結合。

Vector4 to MatrixノードのアウトプットをSetPointTransformに繋ぐことで、
移動値はskeleton_ex側を、回転値はskeleton_origin側を適用できます。

これでジョイント回転値の一括変換は完了です。

おまけ.Houdini20の新機能APEXについて

Houdini20から新たにβ版として実装されている「APEX」についてですが、新たなアニメーション制御システムとして注目されています。

当方ゴジモの過去記事で紹介した、KineFXを使用した武器の持ち替えについて、リグの処理ステートを武器持ち替えアニメーションのタイミングでstashさせて各ジョイントの位置関係を保存しておき、それをswitchノードで切り替えることで実装しておりました。

APEXに関しては当方でもまだあまり修得が進んでいないのですが、サンプルファイルを確認する限りはこういったコンストレイントを使用したアニメーション上の切り替えは容易に実装できるようになるものと思っています。

先ずは以下の公式のチュートリアルが丁寧に説明されていますので、ここを一通り進めていけば理解が進みそうです。

こちらのチュートリアルの作成を進める上で個人的に引っ掛かった点を挙げますと、APEX Edit Graph上のノード内の各パラメーター名と、Skeletonの各ジョイント名、Pack FolderのName、Typeを正しく設定しないとAPEX Scene Animateが正しく表示されなかったところでしょうか。

単なるチュートリアル通りに進めた作成ファイルですが、よろしければ参考にこちらのファイルもご確認ください。


当方ではこれからもこういった技術共有を続けていきますので今後共是非よろしくお願いします!

それでは皆さん、良いHoudiniライフを!!

この記事が気に入ったらサポートをしてみませんか?