TOPのアトリビュートの扱い方入門

Houdini Apperentice Advent Calender 18日目の記事になります。
Houdini18.5.351 での内容となります。

# 概要
TOPのアトリビュートの扱い方の基本的なところを解説していきたいと思います。

TOPで何かをできるようになるための基本知識の一つとして、アトリビュートの扱い方の理解は抑えておきたい要素です。
とは言え SOP のアトリビュートとはまた少し違う違い勝手でもあり、TOPを使い始めたばかりだと戸惑うこともあるかと思います。
そんな、これからTOPを使っていこうと思っている方向けの記事となります。

今回のサンプルシーンと使用するデータは [こちら。] (https://drive.google.com/file/d/1XyVSUqSgZDNeCi1NeuUoJH2O_G-Fk0FL/view?usp=sharing)
シーンの TOP ネットワークに沿って上から順に解説していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/b588a6a2-c510-f1b1-d3ec-911731997066.png)

TOPネットワークの内容は
「フォルダ内のジオメトリファイル達を、
　リダクションして別名で保存し、
　ファイルの情報をCSVに書き出す」
というものになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/fca0f950-17d9-fb76-8da2-2d59f2c6b4ea.png)



この記事はサンプルシーンに沿って解説していきます。
ダウンロードしたhipデータ内のTOPネットワーク上部のボタンからシーンをcookし、ワークアイテムを表すドットを見えるようにしていただくと記事と同じような確認が可能になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/4aa960ca-6e75-d90b-3646-229ba461e556.png)



では、順に解説していきます。





## アトリビュートの確認方法

まずはアトリビュートの確認方法です。主に２つ方法があります。

#### その１ ワークアイテムごとに確認 - 中マウスボタン

ノードをクックしたときに現れるワークアイテムを表わすドット。
このドットをマウス中ボタン長押しかダブルクリックして表示されるウィンドウでアトリビュートを確認可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/1bd7a259-1969-3800-7a1d-db2feec493d9.png)
アトリビュートはピンク色の文字で表示され、ここでは
@directory
@extention
@filename
の3つのアトリビュートを確認できます。

#### その2 シートの一覧表示で確認 - Task Graph Table
ネットワークウィンドウの右上のアイコンをクリックすることで表示される Task Graph Table でも確認することが可能です。
これを使うと、複数のワークアイテムのアトリビュートを一覧で確認することが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/0bdd2e93-a68d-cd82-b993-742255db01fa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/d7eef608-822c-01eb-38b5-ac026f36698e.png)
`※もし Task Graph Table にシートが表示されない場合は何度かノードを選択し直すことで上画像のように確認できるはずです。`







## アトリビュートの種類
#### カスタムアトリビュートとビルトインアトリビュート

アトリビュートは大きく以下の２つに分類することができます。

* カスタムアトリビュート
* ビルトインアトリビュート

『カスタムアトリビュート』は、各種オペレーターから自動で作成されたり自分で作成する一般的なアトリビュート。
先ほどのFilepattern TOP で確認したアトリビュートがそれにあたります。
『ビルトインアトリビュート』は、SOPであれば @P, @N, <span>@</span>uv などに似ていて、事前に役割と名前が決まったアトリビュートになります。

ビルトインアトリビュートはそれほど数はなくヘルプに解説があります。
https://www.sidefx.com/ja/docs/houdini/tops/attributes.html
後で例に出しますが、実際にパラメータ欄で使う時は、頭に"pdg_"を付け加えます。


よくつかうものは

- index : ワークアイテムのインデックス
- output : ノードから出力されるデータのパス
- input : ノードに入ってくるデータのパス

あたりでしょうか。

中でも覚えておきたいアトリビュートが、input と output です。
実際にパラメータ欄でエクスプレションとして使う時はそれぞれ <span>@</span>pdg_input, <span>@</span>pdg_output と記述します。
これらは「各ノードから見た」入力データと出力データのファイルパスとなりネットワークの中で常に値が変わってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/95406267-14cb-32da-cddc-1927baec0c54.png)
例えば、上の図では、
赤文字の@pgd_output と 青文字の @pdg_input は同じ値
赤文字の @pdg_outputと青文字の @pdg_output は別の値
になります。

そしてほとんどのノードは、デフォルトで @pdg_output にあたるファイルを出力し、次のノードに送っています。
Python Script TOP など、オペレーターの性質上、出力ファイルを持たない場合でも、@pdg_input を @pdg_output として次のノードに送るという設定があったりします（下図）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/cb0afe78-8b7a-69e6-312c-87b9fdaf21a4.png)


SOPに慣れていると特殊に感じますが、input, output はTOPでは頻出のアトリビュートなので押さえて置きたい部分です。
中マウスボタンからは以下の部分で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/7242f3c3-83f5-c5d6-42d9-b774dc65eae3.png)



## カスタムアトリビュートの作り方
先程の Filepattern TOP で確認したように、カスタムアトリビュートはオペレーターの種類に従って自動的に作られるのですが、もちろん自分で作成することもできます。
サンプルシーンでは Attribute Create TOP を使って @parcent というアトリビュートを作成してます。
後の工程の PolyReduce SOP で使うための値なのですが、数値として10を設定していますので、すべてのワークアイテムで同じアトリビュート値として10を持つことになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/62dd3e23-9a8c-b650-7276-306c6a517646.png)
今回float型のアトリビュートを作成しましたが、パラメータ（上図下線）を見ると、他の型も確認できます。
TOPで使えるアトリビュートの型は、基本的にここにあるものになります。
ドキュメントを読むと他には、geometry型というものがあるのですが、よくわかっておりません。



## アトリビュートの取得 Python
アトリビュート値を取得したい場合、カスタムアトリビュートとビルトインアトリビュートで方法がかわってくるのでそれぞれ説明します。
#### カスタムアトリビュートの取得 Python
Pythonでカスタムアトリビュートにアクセスする場合は、pdg.WorkItemクラスのオブジェクトを取得し、そのオブジェクトのattribValue メソッドから、アトリビュートにアクセスします。
（ここでいうオブジェクトはpythonオブジェクトのこと）

```Python:カスタムアトリビュート値の取得
value = work_item.attribValue("attributeName")
```

Python Script TOP を使用する場合は、work_item という変数に現在のワークアイテムオブジェクトが入っているので、
それに続けてメソッドを記述すれば、各ワークアイテムのデータを取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/c93169ce-f7a8-1880-c172-cccb5378512f.png)






サンプルシーンの pythonscript ノードでは、filepattern ノードで作られた @directory と @filename を取得して手を加えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/c23e2864-d6d6-23dc-fdc7-8164297d3640.png)


ちなみに Python Script TOP の場合、ワークアイテム（グリーンのドット）ごとに Python の処理が走るので一つのワークアイテムを考慮したコードを書く形になります。SOP でいうと、ポイントごとにVEXが走るのに感覚は近いです。


python script TOP のヘルプからインポートできるサンプルデータをみると以下のような記法もできるようです。

```python:カスタムアトリビュート値の取得その2
value = work_item["attributeName"][0]
```
この記法を使った場合、間違って存在しないアトリビュート名を指定すると、きちんとエラーが返ってくるので、この記法のほうが本流なのかもしれません。




#### ビルトインアトリビュートの取得 Python

Pythonを使ってビルトインアトリビュートにアクセスする方法ですが、先程のカスタムアトリビュートへのアクセス方法とは、やり方が変わってきます。
自分の試した限りでは、カスタムアトリビュートを取得するときに使った attribValueメソッドでは None が返ってくるだけでうまく値を取得できませんでした。

ではどうするのかというと、ビルトインアトリビュート値は pdg.WorkItem のプロパティとして存在しているので、そこからアクセスできます。
（ここでいうプロパティはpythonオブジェクトのプロパティ）

indexは、
work_item.index

nameは、
work_item.name

frameは、
work_item.frame

といった形で、上記のアトリビュートに関してはプロパティとしてビルトインアトリビュート名を記述します。

これらを見ると、プロパティ名 = ビルトインアトリビュート名 と記述すればよいのかというと別のプロパティ名もあり、inputとoutputはについては以下のプロパティでアクセスできます。


inputは、
work_item.inputResultData

```python:inputアトリビュートの取得
value = work_item.inputResultData[0]
```



outputは
work_item.resultData

```python:outputアトリビュートの取得
value = work_item.resultData[0]
```

どちらも、返り値の内容はファイルパスに見えて実は pdg.File クラスのオブジェクトになるので、テキストとして使用したい場合は、string 型に変換する必要があります。

```python:outputアトリビュートをテキストで取得
value = str(work_item.resultData[0])
```

また、アトリビュートをオブジェクトではなく単にテキストとして取得したいだけであれば、Pythonコードの文字列の中にエクスプレションとしてアトリビュートを記述することで、その値をテキストとして取得することも可能のようでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/6084b2d5-d161-8f61-28ef-dabbd953f847.png)



## アトリビュートの作り方 Python

Pythonからカスタムアトリビュートを作成には、pdg.WorkItemクラスのメソッドを使います。
メソッドはアトリビュート型ごとに用意されていて、
例えばfloat型のアトリビュートを作る場合は以下のとおり。[^2]

[^2]: ワークアイテム内に複数の値がある場合、work_item.setFloatAttrib('attribteName', attributeValue, 0) と最後にindexを加えますが、今記事は、導入にむけた記事であることと、@pdg_indexとの混同を避けるための説明がややこしくなりそうだったため、記載してません。

```python:float型のアトリビュートの作成
work_item.setFloatAttrib('attribteName', attributeValue)
```
サンプルシーンでは、この部分になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/823b1861-2d02-c72c-6969-f16e718ec9d2.png)


ここでPython Script TOP を使うとに気を付けたい点を補足。
Python Script TOP でアトリビュートを作成するときは、
パラメータの Evaluate Script During を Generate にしないと正しくアトリビュートが作られません。

※下記の部分について。Houdini19.5では、デフォルトで@pdg_outputは出力されるようです。逆に出力しないようにしたい場合は、"Cachingand Output Files"タブから設定可能のようです。
~~また、Python Script TOP は基本 @pdg_output を出力しませんので、
次のノードで@pdg_outputを利用したい場合は、Copy Input to Outputs のチェックを入れると、
@pdg_input を @pdg_output としてそのまま出力します。~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/ca05a22a-44a7-0555-6372-6d3b4e1b6143.png)









## アトリビュートの使い方 Expression

カスタムアトリビュートをパラメータ欄で使用する場合、＠マークに続けてアトリビュート名を記述すればその値を利用できます。
サンプルシーンの上から４つ目、 ropgeometry ノードのパラメータ Output File にはカスタムアトリビュートを記述しています。
手前の pythonscript ノードで作成したアトリビュート @newdir と @newfile を使って、例えば index 0 のワークアイテムは
$HIP/geo_sample_low/box1_low.bgeo
というファイルが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/975f6a4f-4d2c-4cb1-f72a-24a7ad03ba3a.png)



このパラメータ欄で、実際に出力されるパスを確認したいという場合は下図の手順で確認できます。
①ワークアイテムをクリック
②パラメータ名を中マウスクリック
③表記が実際のパスに変わる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/44d7e0f9-4f65-025d-5d81-2e4997486e77.png)
この「ワークアイテムを選択して何かを確認する」という手順はTOPのデータを一時的に確認するときによく使う方法です。

エクスプレションからビルトインアトリビュートを使う場合も基本的に同じですが頭にpdg_を記載します。
index アトリビュートは、＠pdg_index
output アトリビュートは、＠pdg_index 
といった具合。




#### TOP外でアトリビュートを使用

TOPの外、SOPなどでも、パラメータ欄でアトリビュートを使用する場合は、エクスプレションで記述することになるので＠マークに続けてアトリビュート名を記述すれば値を取り出せます。


当然ですが、TOPコンテキスト外でアトリビュートを利用するためには、ワークアイテムとアトリビュートを記述するノードが紐づいている必要があります。
例えば、サンプルシーンでは、ropgeometry ノードの SOP Path パラメータで /obj/external/output1 を指定しているので、 その output1 ノードの上流にあたる polyreduce1 ノードはワークアイテムと紐づいた状態となってアトリビュートを使えるという感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/6defe7a8-2c31-79a4-f188-28f82004c96d.png)


/obj/external/file1 ノードに目をむけると、ビルトインアトリビュートの @pdg_input を Geometory File として指定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/3a2a3cd9-48c5-3cf8-cea0-fc55c9a98005.png)

この場合は ropgeometry ノードの @pdg_input アトリビュートである
"$HIP/geo_sample_low/box1_low.bgeo"
が、そのまま、/obj/erernal のfile1 ノードに読み込まれることになります。

また、次の poly reduce ノードでは、TOP の attributecreate ノードで先ほど作成したカスタムアトリビュート @percent を記述しており、その値である10がそのパラメータ値としてつかわれます。
※画像に見えるパーセンテージは7.41%ですがパラメータ欄には10が記載されている状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/afe34cee-763f-f75b-1620-6a90858fea1f.png)


ここではTOPネットワークをcookした時のTOP外でのアトリビュートの使われ方を説明しましたが、次に、一時的にワークアイテムを TOP 外のコンテキストと紐づける方法を説明していきます。

#### TOP外で使用しているアトリビュートの値を確認する

TOP ノードのワークアイテムであるドットをクリックして選択した状態にすれば、Houdiniシーン内のノードはそのワークアイテムと紐づいた状態にできます。
その状態では、パラメータ欄にTOPのアトリビュートを使っていれば、そのパラメータ名をマウス中クリックすることでワークアイテムのアトリビュート値を確認できます。
※TOP ノードのパラメータ欄でのアトリビュート確認手順を先ほど説明しましたが、それと同じ感じです。

また今回の処理とは関係ないノードですが /obj/external ノードに下にいる /obj/pdg_output の中の file1 ノードには output アトリビュートの \`@pdg_output\`を記述しているいます。
一旦、/obj/external のディスプレイをオフにして、/obj/pdg_output のディスプレイをオンにして、TOP のノードの任意のワークアイテムをクリックするとその結果(output)が3Dビューポート表示されることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/29a6e849-8242-88ea-f489-19feb269631a.png)


この仕組みで簡単にTOPの結果を確認出来きるのでデバッグに重宝します。

※テキスト型のパラメータ欄へ記述する場合は、バッククォートを付けてエクスプレションであることを明示するのを忘れないようにしましょう。


#### VEXでアトリビュートを使う

VEXでアトリビュートをつかいたい場合は、VEX内にエクスプレションとしてアトリビュートを記述すれば利用できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/ef7645cc-7d15-3407-c604-cd9a37dd7cdb.png)



## ジオメトリアトリビュートをTOPのアトリビュートとして使えるようにする

今度は、SOPのアトリビュートを、TOPのアトリビュートとして使う方法を解説します。
これは、一部しか理解できていないので、その範囲で紹介します。

ジオメトリのアトリビュートを使うということは、ジオメトリデータを読み込む必要がありますが、そのタイミングで、ジオメトリのアトリビュートをTOPのカスタムアトリビュートに変換します。

具体的には、Geometory Import TOP でジオメトリを読み込むとき以下の設定をすることでジオメトリのアトリビュートをTOPのカスタムアトリビュートとして使えるようにすることが可能です。

Set Item Index To : Element Index
Copy From Class : 取得したいエレメントのクラス（下図の矢印のどれか）
Create Work Item For : Each Element

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314518/b293a8e1-ed20-142d-22fe-d3f94734d69c.png)

Point単位でアークアイテムを作成した場合はポイントのアトリビュートが変換され、プリミティブ単位でワークアイテムを作成した場合はプリミティブアトリビュートが変換されるといった具合。
ちなみに@nameなどアトリビュート値単位でワークアイテムを作成するとアトリビュートの変換は行われませんでした。

複数のクラスのアトリビュートを同時に変換するといった方法はあるのかどうかわかってないです（知る限りなさそう）


※ここて変換されなかったジオメトリのアトリビュートは当然TOPで使えないのですが、消えてしまったわけではなく、ジオメトリ内にジオメトリのアトリビュートとしてきちんと残ってます。
あくまでTOPのカスタムアトリビュートとして利用できないだけとなります。

## まとめ

以上、この辺を覚えておくとTOPのオペレーションの役に立つのではないかというところを解説しました。

最後に、ざっとおさらいです。

#### アトリビュートの主な作り方、作られ方


- カスタムアトリビュート
    - 各種ノードで自動的に作られる
    - Attribute Create TOP で作成
    - Python で作成
    - Geometry Import TOP を使うときに変換

- ビルトインアトリビュート
    - 役割と名前がきまっていて作成せずとも存在している


#### Pythonからアトリビュート値にアクセスする方法

- カスタムアトリビュート
    - pdg.WorkItemのattribValueメソッド
    - pdg.WorkItemのfloatAttribValueメソッド等（型ごとにメソッドが準備されている。記事ではふれてませんが…）
    - work_item["attributeName"][０]

- ビルトインアトリビュート
    - pdg.WorkItemの各プロパティからアクセス可能
        - work_item.index, work_item.resultData など

#### パラメータ欄でアトリビュート値を使う

- カスタムアトリビュート
    - ＠に続けてアトリビュート名を記載
    
- ビルトインアトリビュート
    - ＠pdg_ に続けてアトリビュート名を記載
        - 例) @pdg_output

以上、最後まで読んでいただきありがとうございます。
ほぼ絵の無い記事になってしまいましたが、何かしら役に立てれば幸いです。
分からないという箇所などあれば、わかる範囲で補足しますので、お知らせいただければと思います。


# 参考
- 公式ドキュメント　pdg.WorkItem class
    - https://www.sidefx.com/docs/houdini/tops/pdg/WorkItem.html
    
- 公式ドキュメント　Using Attribute
    - https://www.sidefx.com/docs/houdini/tops/attributes.html