【Houdini】フェイスの頂点をいろいろな vex コードで結合する

vex によるプログラム処理 で、あるプリミティブ(ここではフェイス)の頂点と別のプリミティブの頂点を結合してみます。なかなかややこしいので図とともに流れをメモっておきます。

`setvertexpoint`という命令を使います。`setprimvertex`という命令の別名だそうですが、意味が分かりやすいように前者の名前で別名が追加されたそうです。どちらにしろ、とても分かりづらいような。。？むーん、これは覚えるしかない。
http://www.sidefx.com/ja/docs/houdini/vex/functions/setvertexpoint.html

引数としてプリム番号、頂点番号、ポイント番号が登場します。

```c
int  setvertexpoint(int geohandle, int prim, int vtxofprim, int pt)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/b4cfd933-1fd1-cbc9-3e37-ce4637194295.png)
BOXとGRID(1x1)が登場するネットワークを組みました。

## 操作１

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/190da53b-61d6-2fd1-b224-065679ec9be3.png)
図の水色で塗られた場所のポイント（ポイント番号３、プリム番号０・頂点番号２）をBOXの角であるポイント番号５に結合してみます。
なお、ポイント番号とプリム番号はユニークですが、頂点番号はプリムごとにINDEXを持っているので重複があるのに注意します。頂点番号２のポイント（緑色の文字）はBOX側にも面ごとに存在するのが確認できます。

まず、わかりやすさのために、vexのランオーバーはディティールで実験します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/26a6cac5-d193-edb3-bb8f-37318ecf29bc.png)

```c
setvertexpoint(0, 0, 2, 5);
```

上記のようなVEXソースを入力すると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/5da5fb85-da95-4b41-3c4b-071037560475.png)

GRIDが変形し、頂点が結合しました。ここまで問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/1831e6f9-5232-78ff-2b21-fb695bbb13e4.png)
反対から見ると、結合前の頂点が参照していたポイントはそのまま残っています。不必要なら削除しましょう。通常のDCCツールの感覚だとポイントが吸着して同化すると思ってしまうので、注意します。

## 操作２

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/8441d416-81b6-370e-5826-338575efc16f.png)

今度はBOX側の頂点をGRID側に結合してみます。

```c
setvertexpoint(0, 1, 0, 3);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/e2f7cecb-c36b-3f3a-af8d-c93ad53be4fe.png)
結合した、、けれども何か変です。（というか予想されたことですが、）結合元の頂点０が参照するポイントは別の頂点も共有しているので、共有していたフェイス側の頂点は結合されずに取り残されてしまっています。

```c
setvertexpoint(0, 1, 0, 3);
setvertexpoint(0, 2, 3, 3);
setvertexpoint(0, 5, 1, 3);
```
今回はこのように書くことですべての頂点を面に結合することができます。ポイントを共有する頂点が少ない方から結合しに行った方が楽そうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8302/47ccc565-0b64-1c60-db9e-7d9fd77dced6.png)

## いろいろな書き方

ランオーバー（vex 処理の対象にするエレメント）をポイントにするのか、頂点にするのかプリミティブにするのかで色々な書き方ができそうです。BOX 側の3頂点を grid 側に結合する vex コードで上記と同じ動作をする物を羅列してみます。

* ランオーバー：ディティール　グローバル頂点番号

```c
int vtnum[] = pointvertices(0, 5);
for(int i=0;i<len(vtnum);i++)
{
    setvertexpoint(0, -1,  vtnum[i], 3);
}
```
ポイント５に紐づいているグローバル頂点番号を全部取得して、ポイント３に結合しています。グローバル頂点番号とはプリムごとのindex番号ではなく、シーン全体でユニークな頂点番号です。プリミティブをまたいだ処理を行う場合は、グローバル頂点番号を引数にしたほうが処理の記述が楽そうです。（
グローバル頂点番号を引数にする場合、プリム番号は -1 を指定します。）

* ランオーバー：プリミティブ　グローバル頂点番号

```c
int vtnum[] = primvertices(0, @primnum);
for(int i=0;i<len(vtnum);i++)
{
    if(vertexpoint(0, vtnum[i])==5)
    {
        setvertexpoint(0, -1,  vtnum[i], 3);
    }
}
```
プリミティブ内のグローバル頂点番号一覧を得て、それがポイント番号５だった場合に３に結合しています。


* ランオーバー：頂点　グローバル頂点番号

```c
if(@ptnum==5)
{
    setvertexpoint(0, -1, @vtxnum, 3);   
}
```
頂点で vex を回すと `@ptnum` がポイント番号、`@vtxnum` がグローバル頂点番号を返すので簡潔に書けます。

* ランオーバー：ポイント　グローバル頂点番号

```c
if(@ptnum==5)
{
    int vtx = pointvertex(0, 5);
    while(1)
    {
        setvertexpoint(0, -1,  vtx, 3);
        vtx = vertexnext(0, vtx);
        if(vtx==-1) {
             break;       
        }
    }   
}
```

頂点番号が５だった場合、保持している頂点をイテレートしてポイント３に結合しています。


とりあえずは以上ですが、この周辺は（わかりずらい名前の）似たような命令がたくさんあるので、これ以外にも書き方はいろいろあると思います。


# まとめ
念のため追記ですが、この記事は vex でゴリゴリ処理をかくときに参照するためのメモです。こんな事をせずに、通常は `Fuse SOP` 等を使って楽にポイントを結合しましょう。
https://www.sidefx.com/ja/docs/houdini/nodes/sop/fuse.html

それにしてももっと楽に書けないもんか。すべてのポイントの吸着を1処理で書きたい。（そんな命令ないのかな？）