Copy/Stampを卒業しよう！

この記事はHoudini Advent Calendar 2016の一日目の記事です。

#　copySOPのstamp
サンプルとして机を用意しました。まずは初心に帰り、基本中の基本copySOPのstampから。
＊注意
(Houdiniを触り始めたての方は、
https://www.sidefx.com/tutorials/introduction-to-procedural-modeling-lesson-3-copy-me/
などでcopyを理解しましょう)
(分かりやすくするために机のオブジェクトを使用していますが、デフォルトではデータがないので、自分で行う場合はtestgeometryを使ってください)

##　copySOPの基本

オブジェクトはpointごとの配置となります。

▼この机をcopyのInputに入れます。
![copy1.png](https://qiita-image-store.s3.amazonaws.com/0/151589/2ce22868-f821-3606-c9d4-62937c22c98a.png)

5x5のgridを作成し、copyと接続します。

すると机が横に向いてしまいます。
 ![copy2.png](https://qiita-image-store.s3.amazonaws.com/0/151589/fa234679-d25b-59a5-16e0-77650f700a5d.png)


こうした場合Transform Using Template Point Attributesのチェックを外したり、copyするobjectに対してtransformを行い回転させたりします。

　![copy2.5.png](https://qiita-image-store.s3.amazonaws.com/0/151589/c6d5bd0d-eabd-4d9d-b6e0-b651af0264e1.png)

 
しかし、この方法はあまり好ましくありません。なぜならば、今このときを限って言えば上手く動作しているのですが、ひとたびtemplateのデフォームが発生した場合、意図せぬ配置になる可能性があるからです。
![copy3.png](https://qiita-image-store.s3.amazonaws.com/0/151589/0e1ae460-d730-80af-f9a5-bd7938e8d49e.png)

▼あらぬ方向に回転がかかる
![copy4.png](https://qiita-image-store.s3.amazonaws.com/0/151589/347c8977-cb86-8989-f941-d607b7e5a731.png)
 
▼Transform Using Template Point Attributesのチェックを外した場合、まったく動かない
![copy5.png](https://qiita-image-store.s3.amazonaws.com/0/151589/5aa79411-2f57-8fc7-f649-9745a4e37873.png)

では、どうすべきか。ここはpointのattributeにupベクトルを与えてあげましょう。(解説は後)
 ![copy7.png](https://qiita-image-store.s3.amazonaws.com/0/151589/d15ce4d4-fdee-8667-6843-e326a08ba037.png)


![copy6.png](https://qiita-image-store.s3.amazonaws.com/0/151589/07461b87-3f97-7ce0-8c3a-6f7ee8190c92.png)

 
 では、机ごとに少しランダムな回転をかけるためにstampを使いましょう。
![copy8.png](https://qiita-image-store.s3.amazonaws.com/0/151589/a9ec61de-e67a-b411-a7a1-b3bc7e5f2e18.png)
StampInputsをオンにしてVariable1に変数名を与えます。値は適当に－22.5～22.5度にします。
そして、transoformのRotateに以下のstamp()を入れます

 ```
stamp("../copy","rot",0.0)
```
机に少しランダムな回転がかかりました。めでたしめでたし。
![copy9.png](https://qiita-image-store.s3.amazonaws.com/0/151589/2f120e85-6ede-7aa7-28a7-7cf8af7d6b60.png)

##　ん？何か重くないか？

しかし、このstamp処理は重いです。数個なら問題はないでしょう。しかしコピーの数が数百となった日には…。
しかしcopyは悪くありません！諸悪の根源はstamp！であれば、stampを消してしまえ！

#　PointAttribute
代わりにPointAttributeを利用しましょう。
(ヘルプ：http://www.sidefx.com/docs/houdini15.0/copy/instanceattrs)
		
| Attribute name | Data type | Description |
|:-:|:-:|:-:|
| orient | float4(quaternion) |  コピーした時の方向 |
| pscale | float| xyzに均等なスケール |
| scale | float3| xyzに不均等なスケール  |
| N |  vector |  法線 |
| up |  vector | Upベクトル  |
|  v | vector	  | Velocity  |
|  rot |  float4(quaternion)  |  回転 |
| trans |  vector |  移動 |
| pivot  | vector  |  ピボット |
|  shop_materialpath | string  |  マテリアルのpath |
|  material_override | string  |  マテリアルの上書きパラメータ |


この表で気を付けなければならないのは、「型(データタイプ)」です。
コピー時に型が違うと、名前が同じであっても完全に無視されます。


#　実践　PointAttributeを利用する
基本となる移動、スケール、回転について紹介していきます。以下のVEXはcopySOPのtemplateに入れる手前に作成したAttribute Wrangle(Run Over:point)で実行していきます。
例は移動量などで2を用いていますが、@Timeなどを上手く利用すると徐々に大きくなどの動作も可能です。

▼ネットワークもすっきり
![copy10.png](https://qiita-image-store.s3.amazonaws.com/0/151589/49700096-d147-0010-75e3-bf72951e55e4.png)



##　移動
Y方向に2移動します

```
v@trans=set(0,2,0);
```
##　スケール
pscaleとscaleは型が異なります。
pscaleは直接floatを、scaleはvectorなのでset()などを利用します。
###　均等に2倍
```
@pscale=2;
```

###　Y方向に2倍
(0倍だと消えてしまうので、XとZにも1倍を与えてやる必要があります)

```
@scale=set(1,2,1);
```
##　回転
軸をupベクトルにしています。
ident()はただの単位行列、upベクトルとgridからの法線を利用して回転行列を作成します。

```：
matrix3 matx = ident() , maty = ident() , matz = ident();
vector4 @rot;
v@up = set(0,0,1);

float anglex = radians(90);
vector axisx = cross(normalize(v@up),normalize(v@N));
rotate(matx, anglex, axisx);

float angley = radians(20);
vector axisy = cross(normalize(v@up),normalize(axisx));
rotate(maty, angley, axisy);

float anglez = radians(10);
vector axisz = cross(normalize(axisx),normalize(axisy));
rotate(maty, anglez, axisz);

matrix3 mat=matx*maty*matz;

@rot= quaternion(mat);
```
いきなり直接クォータニオンを利用しない理由は、クォータニオンの戻り値はvector4のため、matrixのように複数の座標系を乗算することができないからです。
そこで今回は先にmatrixで計算を行って、そのあとからクォータニオンでvector4の形に変換させています。

#そのあとは

PointAttributeを利用すると、ノードもすっきりしてインスタンスの処理が行いやすくなります。
下準備ができたら、あとはそのままcopySOPやInstanceSOP、Delayed Loadなど好きな手法でコピーしてみましょう。
http://blog.livedoor.jp/fujitarium/archives/52412058.html
(外部リンク：fujitarium「Houdini検証 インスタンス＆コピー」)

マテリアルをpointごとに変える場合は、shop_materialpathとmaterial_overrideを利用します。
考え方は同じで、pointに対してmaterial情報を書き込みます(Wrangleでなくmaterialノードを利用するほうが良いでしょう)。このattributeはpointでなく、オブジェクトに対して適用したいので少し工夫が必要です。copySOPのattributeタブにおいて、Use Template Point Attributesを有効にし、To Primitiveでshop_materialpath material_overrideを入れます。そうすることでマテリアルをpointごとにバラバラにすることも出来ます。

![deskout5.png](https://qiita-image-store.s3.amazonaws.com/0/151589/49898260-710a-0764-4049-172813e7f6d0.png)

以上