16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Houdini ApprenticeAdvent Calendar 2018

Day 14

カスタムノードシェイプを作る

Posted at

ノードの形状には色々な種類があり、好きな形状に変更することができますが、このノードシェイプを自作して追加することもできます。
nodeshape.PNG

作成方法は英語のまとめ記事がいくつかありますが、私はこちらの動画を参考にしました。
シーンファイルもリンク先から取得できます。
https://vimeo.com/221182957

今回はこの動画をベースに、自分なりの工夫も併せて解説させていただきます。

##ファイル

ノードシェイプのファイルは JSON 形式になっています。

デフォルトのノードシェイプのファイルは以下に入っています。
$HFS(Houdiniインストールフォルダ)\houdini\config\NodeShapes

新しく作成したファイルは、下記にフォルダを作成して配置すると、次回起動時から認識されます。(windows10)
Documents\houdini17.0\config\NodeShapes

###ノードシェイプの要素

JSONの仕様として、{}で囲んだ中に、要素の名前と値をセットで記述します。

{
  "aaa": "hello",
  "bbb": 1,
  "ccc": true
}

デフォルトのノードシェイプのJSONファイルを見ると、要素として下記を定義していることが分かります。

  1. ノードシェイプの名前
  2. ノードシェイプのアウトライン
  3. Bypass フラグ用アウトライン(黄)
  4. Lock フラグ用アウトライン(赤)
  5. Template フラグ用アウトライン(紫)
  6. Display フラグ用アウトライン(水色)
  7. input のドットが配置されるライン
  8. Output のドットが配置されるライン
  9. オペレータアイコンの位置

node_flag.png

それでは2~6のアウトラインについて作成していきます。

##アウトラインの書き方

Viewを Front にして、Curve SOP でアウトラインを描いていきます。
大きさや位置はシーン内の他のサンプルを参考に、x軸は0-1の間、y軸は0より少し上あたりが中心のようです。
もっと大きいシェイプを描けば、巨大なノードシェイプも作成できます。

Viewに下書きを表示するには、Display Options の Background タブで front を選んで画像ファイルを選択します。

background.PNG

ここが重要なのですが、 アウトラインは一筆書き で描きます。
セパレートされた形状を持つことはできません。
ただし、書き方次第で内側に線を追加したり、中ヌキにすることはできます。

ここに2重の円がありますが、
巻き込むように描けば内側に線が入り、三日月のように描いていけば中ヌキになります。
circle.png

複数の形状を合成したアウトラインを作成する場合、booleanを使っても良いのですが、point番号が意図しない順番になってアウトラインが崩れやすいので、面倒ですが Snap を使って元形状の上から書き直すのが結果的には早いです。
outline.PNG

###eps
adobe illustrator からの epsファイルも使用できます。
eps.PNG
ここでは Bypass フラグ用のアウトラインとして、星型のシェイプをepsで読み込んでみました。
eps_flag.PNG
ついでに他のフラグ用のアウトラインも丸っぽい形状で作成しました。

flagの形状は、元のアウトラインからbooleanで切り抜いても構いません。
また、この作例の星形状や丸形状のように、元のアウトラインの上に乗せただけの状態だと、
フラグONの時だけ実体化し、OFFの時は線画のみ、のように見えますので、使い分けを考えるのも楽しいかもしれません。
(後述)

##カーブから2D座標を取得

動画のリンク先のシーンから"outline"の下にある Point Wrangle を見てみます。
nodegraph.PNG

Point
s@pos = sprintf("[%g,%g]", @P.x, @P.y);

setdetailattrib(0, "outline", @pos, "append");

if (@ptnum < @numpt-1) {
    setdetailattrib(0, "outline", ",", "append");
}

ノードシェイプのJSONの書式に従った [X座標,Y座標] という記述になるように、Point のX座標とY座標を、sprintf を使って書き込んでいます。
通常の printf はコンソールに結果を出力しますが、 sprintf は結果を文字列として返します。
フォーマットを使って %g のところに後の引数が入りますので、 [@P.x,@P.y] が各頂点ごとに @pos として書き込まれます。
それをさらに Detail にカンマで結合して入れています。

同様に、他のアウトラインも座標を Detail に入れています。

###angle

inputs、outputs に関しては、座標のほかに angle という値も作成して加えています。ノード接続のラインの角度に使うようです。

angle.PNG

polyframe SOP で

  • Normal Name:up
  • Tangent Name:N
    を作成して
Point
@N = cross(@N, {0,0,1});
Point
@angle = degrees(atan2(@N.y, @N.x));
@angle = (@angle + 360) % 360;

オペレータアイコンの位置に関しては、対角線の2点のみで良いようです。

##出力

最後にこれらを merge したあと、Detail に入っている各座標情報を、JSONファイルの書式で結合しています。

Detail
string fullpath = opfullpath("..");
string path[] = split(fullpath, "/");

string json_string = "{\n\"name\": \"";
json_string += path[len(path)-1];
json_string += "\",\n\"flags\": {\n\t\"0\": { \"outline\": [";
json_string += detail(1, "flag0_outline");
json_string += "] },\n\t\"1\": { \"outline\": [";
json_string += detail(1, "flag1_outline");
json_string += "] },\n\t\"2\": { \"outline\": [";
json_string += detail(1, "flag2_outline");
json_string += "] },\n\t\"3\": { \"outline\": [";
json_string += detail(1, "flag3_outline");
json_string += "] }\n},\n\"outline\": [";
json_string += detail(1, "outline");
json_string +=  "],\n\"inputs\": [";
json_string += detail(1, "inputs");
json_string +=  "],\n\"outputs\": [";
json_string += detail(1, "outputs");
json_string += "],\n\"icon\": [";
json_string += detail(1, "icon");
json_string += "]\n}";

setdetailattrib(0, "json_string", json_string, "set");

ノードシェイプの名前は親ノード名から取っています。
あとはひたすら、"をエスケープしながら結合していってます。強引で面白い!

こうして出来上がった文字列を、参考動画では右クリックからコピーして、新規のテキストファイルに貼り付けています。
detail_copy.PNG

せっかくなのでそこも自動化したいなーと思い、Pythonで出力ノードを作成してみました。

node = hou.pwd()
geo = node.geometry()

import os
hippath = hou.hipFile.path()
dirpath = os.path.dirname(hippath)
filename = "/"+str(node.parent())
extension = ".json"
filepath = dirpath + filename + extension

file = open(filepath,'w+')
json_string = geo.stringAttribValue('json_string')
file.write(json_string)
file.close()

このノードをDisplayすれば、シーンファイルと同階層にJSONファイルが出力されます。

出来たアイコンをNodeShapesのフォルダに入れて、
Houdiniを再起動すれば、ノードシェイプが増えました!
fix_0.PNG
fix_1.PNG

メリークリスマス!

##おまけのSVG

テキスト形式のベクター画像ならSVG、という事で、SVGファイルも出力してみます。

HoudiniのオペレータアイコンもSVGで出来ていますので、中身を拝見すると、Inkscape というサービスを使って作成されていることが分かりました。(https://inkscape.org/)
リッチで便利そうなのですが、今回は強引に手打ちをするため極力シンプルな構成にしてみます。

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" width="48px" height="48px" id="svg47377" xmlns="http://www.w3.org/2000/svg">
<path
    d="M 14.5609 23.7006 L 14.5609 23.7006 15.466 23.0671 (略) Z"
    fill="gray" />
</svg>

SVGの仕様について、ざっと調べた感じでは以下のようでした。(間違ってたらすみません)
最初の<svg>のところでカンバスの大きさやsvg公式へのリンクが書かれています。
次の<path>のところで描くカーブを座標で指定しています。座標は半角スペースで繋いでいきます。
Mは、カーブの始点です。Lはカーブを構成するラインです。Zはカーブ描画終了を意味します。
fillは塗りカラーです。

先程のカスタムノードシェイプのアウトラインのカーブを使って、0-1では小さすぎるので拡大したり、回転したりしてみます。

JSONの時と同じようにpoint座標を Detail に入れていきますが、今度は半角スペース区切りにします。

書式の構築の部分で、SVGの書式になるように変更し、
最後は同様に出力用Pythonノードを繋げて、拡張子を.svgにしたら完成です。

svg.PNG


今回作成したJSONファイルは以下に置いておきますので、ご自由にどうぞ。
tree.png
flagのアウトラインをbooleanせずに配置すると上図のようになり、ONの時だけ実体化する印象になります。
https://www.dropbox.com/s/fp2au957dyo1rs2/tree.json?dl=0

doggie.PNG
flagを元アウトラインからbooleanで抜いた場合は、こちらのようになります。
こちらのわんこも配布しています。
https://www.dropbox.com/s/7vle26nzm34nfhx/doggie.json?dl=0

シーンファイルは動画のURLから取得できます。
https://vimeo.com/221182957

ありがとうございました ( °ᴗ° )

16
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?