Intrinsicを使おう

この記事は [Houdini Advent Calendar 2017](https://qiita.com/advent-calendar/2017/houdini)の16日目の記事です。

昨年はビビって書けませんでした。
今年もレベルの高い記事ばかりでビビっていますが、書かせていただこうと思います。
拙い文章ですが、目を通して頂けると嬉しいです。


今回使用するデータは[こちら](https://drive.google.com/file/d/12iqY58VyA8ZRdCG4sqcaDNc1gmy6HOL3/view?usp=sharing)です。 ver. Houdini 16.5.268

#Intrinsicとは？
Intrinsic...最近Houdiniのセミナーでもよく耳にするようになりました。
Advent Calendarでもちょいちょい登場していますね。
巷では**"隠しアトリビュート"**と呼ばれていたり。

翻訳サイト等で"Intrinsic"の意味を調べてみると
**本来備わっている,　固有の, 本質的な,**
といった意味だと。

つまり
カスタムアトリビュートとは違い、ユーザーが定義して付与されるものではなく
ノード(primitive, detail)がクックされた次点で自然に持っている情報がIntrinsicアトリビュートということになります。

boundの値やpirimitive内にいくつvertexを持っているか、
メモリをどれくらい使用しているか、といった値を持っていたりします。


そんなIntrinsicアトリビュートの使い方とちょっとしたtipsを紹介できればと思います。

#Intrinsic を使ってみる
Intrinsicアトリビュートには
Primitive Intrinsic や Detail Intrinsicがあり、各々の値を利用することができます。


これらはGeometry Spreadsheetから値を確認することができますが、普段は表に出ていません。
**[Intrinsics]**のプルダウンメニューからカスタムアトリビュートのように値を表示させます。
![spreadsheets_edit.png](https://qiita-image-store.s3.amazonaws.com/0/212535/6cc76443-2638-49f5-8d5e-bd93a0492520.png)

intrinsic:****のように表示されました。
![spreadsheets_intrinsic.png](https://qiita-image-store.s3.amazonaws.com/0/212535/de9feb26-29f0-3828-b33d-e0f81a263bfb.png)


扱っているデータによって持っているintrinsicの種類は異なります。
表示してみると、値の多くがグレーの文字で表記されていることがわかります。
これらはRead Onlyなデータで、カスタムアトリビュートと違い値を書き換えることはできません。

実際にVOPやWrangleで使うためにはノードや関数を使います。
それらの詳しい使い方に関してはヘルプを参照してください。
よく使うのはここらへん
http://www.sidefx.com/docs/houdini/nodes/vop/primintrinsic.html
http://www.sidefx.com/docs/houdini/nodes/vop/setattrib.html
http://www.sidefx.com/docs/houdini/vex/functions/primintrinsic.html
http://www.sidefx.com/docs/houdini/vex/functions/setprimintrinsic.html

##Measured perimeter, area, volume
MeasureSOPで取得できる perimeter, area, volumeといったアトリビュートは
Intrinsicアトリビュートにすでに存在しています。従って、measureSOPを使用しなくとも
これらの値はIntrinsicから取得できてしまいます。
wrangleやvop内の処理でアトリビュートとして残す必要のない場合に便利です。
![measure.png](https://qiita-image-store.s3.amazonaws.com/0/212535/39720f27-8ce3-7b47-97f1-7e7080d5caf4.png)


##Transform
Intrinsic Transformやpivotを使うことによる利点は
ジオメトリのポイント一つ一つに処理をすることなく、Packed primitiveという１つのポイントに対してtransformをかける処理ができるところです。

今回はレイアウトされたモデルに対して編集を加えたいとき、原点に戻して編集後またレイアウト位置に戻すといったことを例に行ってみます。

このように配置されたモデルがMayaからAlembicできたとします。(適当すぎる例ですみません…)
![hands.png](https://qiita-image-store.s3.amazonaws.com/0/212535/01fd6a88-696f-f897-112c-3841edb258fa.png)

後々、この腕ごとにPackをしたいので
pathアトリビュートからモデル別にnameをつけたいと思います。

```vex:primitiveWrangle
string list[] = split(s@path, "/");
s@name = list[1];
```

PackedAlembicに適用されているTransformをoffにするため、intrinsic: **abcusetransform**の値を0にします。

```vex:primitiveWrangle
setprimintrinsic(0, "abcusetransform", @primnum, 0, "set");
```
原点で作成されTransformで配置をされていればここで原点に戻るはずです。
今回は同じモデルを使っているため、全部重なってしまっています。

![hands_origin.png](https://qiita-image-store.s3.amazonaws.com/0/212535/9717e12a-3fae-a52c-eb72-cc1f5c68dafa.png)
これで、すべてのモデルが原点に配置され、回転も切ることができました。

各々編集を加えていきます。
![voronoi.png](https://qiita-image-store.s3.amazonaws.com/0/212535/55b0e606-1766-d6f6-258b-1737fc11efe8.png)
今回は手首の外側を細かく砕くようにしてみました。
voronoiを使用して分割しているため、分割後nameアトリビュートがvoronoiによって上書きされてしまいます。
そこで、最初に作ったnameアトリビュートをname_oldとして残します。
更に、foreachで計算されたあと、各iterationごとに同じようにnameがpiece*と設定されダブりがでてしまいます。
それを防ぐために最初に作ったnameアトリビュートをpiece*の後ろに付けることにします。

```vex:primitiveWrangle
string name = prim(1, "name", 0);

s@name += "_" + name;
s@name_old = name;
s@path = prim(1, "path", 0);
```
![net1.png](https://qiita-image-store.s3.amazonaws.com/0/212535/beb19551-1dad-0f41-a761-7b298a7c537e.png)

![voronoi_sum.png](https://qiita-image-store.s3.amazonaws.com/0/212535/50b7d76d-7c42-96e0-eb3f-bdfb0d757966.png)
foreachの結果はもちろん、破片のseedはiterationを使って変えているのでバラバラですが、
モデルは原点に配置され、重なったままです。

これをもとの配置に戻していきます。
先ほど作ったname_oldでPackしていきます。

Packしたものに元のPackedAlembicのポジションを与えます。

```vex:Pointwrangle
@P = point(1, "P", @primnum);
```

そして最後に元の回転を適用します。
Transformは書き換えが可能なIntrinsicなので…

```vex:PrimitiveWrangle
matrix xform = primintrinsic(1, "packedfulltransform", @primnum);
matrix scale = ident() * 100;
matrix3 rot = matrix3(xform * scale);
setprimintrinsic(0, "transform", @primnum, rot, "set");
```

![replace_hands.png](https://qiita-image-store.s3.amazonaws.com/0/212535/b36f7d9f-fc8f-1625-faf6-635393a0bd31.png)
戻りました！
モデルの編集具合によってはPivotをいじる必要があるかもしれません。

TransformとPackを使った操作はこのような配置に使用するほかに
ProxyModelで行ったPack RBDシミュレーションによる移動値と回転をディティールのあるハイポリモデルに転写することにも使えます。



##Bounds
Intrinsic boundsを使って破片を大きさによって選別する手法を紹介します。
![replace_hands.png](https://qiita-image-store.s3.amazonaws.com/0/212535/b36f7d9f-fc8f-1625-faf6-635393a0bd31.png)
先ほどのTransformで使った手の破片を、大きさ別に選別していきましょう。
破片ごとにPackされたモデルを用意します。

boundsは配列アトリビュートになっていて、XYZそれぞれの最小値最大値を格納しています。
最小から最大の距離を測ればBoudingBoxの高さ、幅、奥行きがわかります。
更にそれらを掛け算すれば体積も求めることができます。
Wrangleで計算しましょう。

```vex:PrimitiveWrangle
float bounds[] = primintrinsic(0, "bounds", @primnum);
//Minimum All
vector min_all = set(bounds[0], bounds[2], bounds[4]);
//X_max
vector x_max = set(bounds[1], bounds[2], bounds[4]);
//Y_max
vector y_max = set(bounds[0], bounds[3], bounds[4]);
//Z_max
vector z_max = set(bounds[0], bounds[2], bounds[5]);

float X = distance(min_all, x_max);
float Y = distance(min_all, y_max);
float Z = distance(min_all, z_max);

//to Size
vector size = set(X, Y, Z);
//to Volume
float volume = X * Y * Z;
//export
v@size = size;
f@volume = volume;

```

size と volume というアトリビュートで外に出せば。あとは条件式で分けることができます。
![split_big_small.png](https://qiita-image-store.s3.amazonaws.com/0/212535/71d08005-ed61-0720-62da-c20913079786.png)



#おわりに
intrinsicはまだまだ沢山ありますが、今回はざっくりと簡単にどういうものか紹介させていただきました。

intrinsicを使うことで、既にある情報をもってくることはノード数の削減やカスタムアトリビュートの整理につながります。
また、今まで自作で作っていたけど実はもうIntrinsicとして存在していたなんてこともあるかもしれません。
知っていると知っていないでは大きな違いだと思うので、Houdiniを始めたばかりでIntrinsicの存在を知らない人たちが
知るきっかけになればと思います。

少しでも制作のアイディアに役立てば嬉しいです。

ここ、間違ってるよ！なんて所はこっそり教えてください…。