EnumerateとPackのススメ
enumerateノードとpackノードを使ったいくつかの用途

enumerateノードについて
enumerateノードは指定の要素ごとに0から始まるindexを割り振ることができる。
Houdini Document| Enumerate geometry node
https://www.sidefx.com/docs/houdini/nodes/sop/enumerate.html
index割り振りとしての機能
indexを取得する用途
基本的な使い方としてコンポーネントごとの番号をアトリビュートとして作成できる。
VEXを書いた際に記述する機会の多い「以下のようなポイント番号、プリミティブ番号を保持する処理」と同等になる。i@index = @pointnum;i@index = @primnum;


アトリビュート基準のindex割り振り機能
enumerateノードの優れた機能として、「アトリビュートの値を参照してindexの割り振り」機能がある
アトリビュート値を基準とした疑似的なgroupのような用途
同一のアトリビュートを持つコンポーネントごとにindexを割り振った、groupのような使い方もできる
- fabなどからfbxをダウンロード(画像はEuropeanBeech/SM_EuropeanBeech_Field_01)
- (option)deleteでLOD0以外を削除、AttribDeleteで頂点カラーを削除
- enumerateノードで piece attributeをshop_materialpath
- (option)ColorノードでColorType/ Random from Attributeでindexごとにランダムカラーを割り振り

この「indexによるグループ分けはgroupを作成する場合に対していくつかのメリットもある
不特定多数を対象に処理の分割
boolであるgroupと異なりintで管理できるidは不特定多数の分割対象に対して処理を実行しやすい。
例えば、以下のように入力メッシュにアサインされているマテリアルごとにforeachで処理の単位を分けることができる

COPとの親和性が高い
Copernicusにはidの値からmaskを作成する機能があるのでmaskでわけたいメッシュごとにindexを割り振っておくとマスクわけが楽にできる

などの利点がある
mesureと組み合わせて要素に合わせたソート
アトリビュートで割り振った場合、値が小さい方からindex付けをしてくれるのでmeasureと組み合わせることでmeasureで測定した値、たとえば面積に準ずるソートなどもできる。
- measure:primごとの面積(Area)を取得
- primwrangle:index化するためにareaを100倍(int型にする必要があるので)
- attribcast:areaをint化
- enumrate:areaをPieceAttributeに指定 これでareaが小さい方からindexが作成される
- sort:PrimをPrimitiveSort/ ByAttribute、Attribute/ indexで指定してPrim番号をindex順に並び替え
- Color:RampFromAttributeでindex順に色付け

Packノードについて
ジオメトリをインスタンスとして扱うためのノードで軽量化などのメリットがある
Houdini Document| Pack geometry node
https://www.sidefx.com/docs/houdini/nodes/sop/pack.html
packの特徴
Packは複数のコンポーネントを一つのコンポーネントとして扱うための方法。
CopyToPointsで作成できるインスタンスやAssebleの設定で作成できるpackもこの状態。

packには特に効率化に関してのメリットが有り、
描画の高速化
背景配置時のオブジェクトなどをpackしておくと簡易表示にすることで描画を高速化するできる

処理の最小化
Pack中に設定したアトリビュートはunpack時に含まれるコンポーネントすべて反映することができるので、例えば「HoudiniEngine用にunreal_materialアトリビュートを対象のプリミティブに設定する」みたいな処理を最小限の実行回数で行える

配置がしやすい
pack状態は含まれるコンポート全体で位置情報を持つので配置などが行いやすい

といったメリットがある。
アトリビュートによるPack分け
このpackだがPackノードの設定項目で指定の要素でpackされたコンポーネントを作成することができるので前述のenumerateと同じようにgroupのような扱いができる。

packされたコンポーネントではpack単位で行われるのでたとえばアサインマテリアルの情報を参照するなどの、まとまりごとに一度しか行わない処理と非常に相性がいい。
アサインされたマテリアルごとに処理を行う
packされたコンポーネントではwrangleなどの処理が一度しか実行されないので、共通のアトリビュートをもつコンポーネントの中でループの回数が最小限で済む
例えば以下の例ではこれでマテリアルごとに実行したい処理をすべてのPrimitiveではなく、マテリアルごと4回のループで実行できる
- FBXを読み込み
- PackノードのName Attributeにアサインされているマテリアル名である「shop_materialpath」を指定
- primWrangleでマテリアルごとに実行したい処理を作成 (ここでは「material名 + bc」をbc_textというアトリビュートで保存)
- unpaclk時に bc_tex アトリビュートをTransfer Attributeに設定。

PackとEnumerateの組み合わせ
ここまで述べた通り、packもindexも毛色の違うgroup分けとして利用できるのだが、組合わせての利用もしやすい
indexによるPack
indexでpackの参照条件がアトリビュートとして指定できるのでenumerateで分けておくとpack、unpackがしやすい。
以下の例では先にenumerateでindexをつけておき、高速に処理したいときにindex準拠のPackを行っている。

Packグループの保存用途
また逆にpackした状態でenumerateするとpackごとにindexを割り振ってくれるのでpackの状態を保存できる。
以下の例ではPackでの処理中にenumerateでindexを保存しておいて、あとから再度Indexを基準にして再度Packに利用する。

Packオブジェクトをindexでコピー
CopyToPointsでPackしたオブジェクトをindex付けし、indexによってコピーすると楽
- CopyしたいPack済みオブジェクト単位でenumerateでindexを振っておく
- コピー対象のpointに同様のindexを割り振る
- copy to pointでPieceAttributeにindexを指定

Packを使うとループ処理がかなり高速になります。
Enumerateを使うとPack、Unpackがかなりしやすくなります。
indexで処理単位を分けるのは一気にプログラミング的な扱い方になってしまうものの、いろいろな用途に使え、慣れると処理を分けるのが迅速になり、Packとの組み合わせで処理自体も高速化できるので便利です。

fish_ball
プロシージャル魚類