CarveSOPをAttributeで制御したい。

Houdini Advent Calendar 2022 22日目の記事です。


久しぶりにHoudini Advent Calenderに参加させて頂きます！
特に面白いことしてませんが、どうかお手柔らかに…


>**環境**
Houdini 19.5.303

# はじめに
みなさんはラインを使った表現をアニメーションさせる際、どういったアプローチを取りますか？
POPやsolverSOPでシミュレーションしたり、トランスフォームで地道に…等
手法は様々かと思います。

私はラインのアニメーションをする際CarveSOPを使うことがあります。
しかしながら！既存のCarveSOPは複数のラインを扱う際に、１本１本へのアクセスができないのです。(おそらく)
そのせいか、アニメーションにばらつきをもたせたり複数のラインを１つのノードで扱うとき少々不便におもうことがあります。

今回はCarveSOPでPolyLineを扱う際の挙動をWrangleを使用して再現しつつ、
Attributeを使って操作できるようにしていきたいと思います！

したがって正確には、、、
CarveSOPをAttributeで制御というよりは、CarveSOPの挙動をwrangleを用いて再現しつつ
Attributeで制御できるように組み立てよう！です！


# CarveSOPとは…
周知の事実ではありますが…
CurveではありませんCarveです。
>**Carve**  … 彫る、切る、刻む

CarveSOPドキュメント [**JP**](https://www.sidefx.com/ja/docs/houdini/nodes/sop/carve.html) [**EN**](https://www.sidefx.com/docs/houdini/nodes/sop/carve.html)

>Carveノードは、フェースやサーフェスタイプ、ポリゴン、Bezier、NURBSで動作します。 Carveノードを使えば、プリミティブをスライスしたり、複数断面にカットしたり、ポイントや断面を抽出することができます。

CurveSOPは曲線や面を**生成**するのに対して、CarveSOPは作られたものに対して**処理を施す**ノードですね。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212535/9fc4f0c3-21b1-8051-fe22-7945552edd84.gif">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212535/b3044ef0-525a-434c-3340-20bd6c3e574a.gif">

左はMeshに対して、右はpolylineに対してCarveSOPを適用した例です。


今回はPolyLineに対する挙動について取り扱っていきます。



# やってみる

必要な項目を考えてみます。
1. PolyLineをスライスする位置を定義するアトリビュートの用意
1. アトリビュートに基づいてPolyLineに処理を施す

大きくはこの２つです。

## アトリビュートの準備

ということで、まずはスライス位置を決めるアトリビュートを用意します。
アトリビュートの用意は特別なことはしておらず、
ばらつきを持たせることを目的とした任意のアトリビュートを用意するだけです。
CarveSOPにならって、First U(頭から)とSecond U(お尻から)の両方からスライスできように作ります。

範囲が0~1のプリミティブアトリビュートを用意し、プリミティブのパラメトリックUV座標を用いてスライス位置を決めていきます。
First UとSecond U用の2つ用意します。

## PolyLineスライス処理

メインの処理はここになります。

```:RunOver : Primitive
removeprim(0, @primnum, 0); //インプットジオメトリのプリミティブのみ削除

float u = f@__u; //First U用任意のアトリビュート
float su = f@__su;　//Second U用任意のアトリビュート

int count = i@__count; //予め算出しておいたプリミティブのもつポイントの数アトリビュート

//--------ポイント交差処理--------
float t;
float t1;

if(u <= su)
{
    t = u;
    t1 = su;
}else{
    t = su;
    t1 = u;
}

//--------新たにプリミティブを定義--------
int newprim = addprim(0, "polyline");

//--------First Uを作成--------
vector uPos = primuv(0, "P", @primnum, t);
float attr_utop = primuv(0, "__rstu", @primnum, t);
int attr_id = primuv(0, "__id", @primnum, t);

int upt = addpoint(0, uPos);

setpointattrib(0, "__top", upt, 1, "set"); //First U 抽出用

addvertex(0, newprim, upt); // プリミティブに追加

//--------FirstUとSecondUの間に存在するポイント--------
int primpts[] = primpoints(0, @primnum);
for(int i = 0; i<count; i++)
{
    float u_value = point(0, "__rstu", primpts[i]);
    addvertex(0, newprim, primpts[i]); // プリミティブに追加
    
    if((u_value < u && u_value > su || u_value < su && u_value > u)!=1)
    {
        removepoint(0, primpts[i]); //範囲外のポイントを削除
    }    
}

//--------Second Uを作成--------
vector suPos = primuv(0, "P", @primnum, t1);
int supt = addpoint(0, suPos);

float attr_ubottom = primuv(0, "__rstu", @primnum, t1);
int attr_id1 = primuv(0, "__id", @primnum, t1);

setpointattrib(0, "__bottom", supt, 1, "set");  //Second U 抽出用

addvertex(0, newprim, supt); //プリミティブに追加


```

ドンと載せてしまいましたが、
処理の内容を簡単なgifにまとめると
このような処理が起こっていることになります。

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212535/82fb10cd-8d30-3dfb-25f5-033636d36ff7.gif)


## できた
![Videotogif (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212535/caf6e130-8a4d-bf00-b0f0-1cb9ef211489.gif)
![growth.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212535/8ae28439-16e1-f7f4-08b7-14666eece382.gif)
わーい




# おわりに
あとはインプット時のアトリビュートを補完してあげれば
その後の処理にも対応できるアセットとして完成するはずです。

処理速度を求めるとなると、もっと最適な解はあるかもしれませんが
とりあえずのところ、勘弁してください…。

少々雑ですが、HDAとして簡単にまとめたものもダウンロードできるよう用意したので
見てみてください。

[hipこちら](https://drive.google.com/drive/folders/1LAhj6lc6bOQNDgWWs7WDtRXiaWRj2TB6?usp=sharing)

ここまで読んでいただきありがとうございます！！
良いHoudiniライフを～