この記事について
TouchDesignerやってるけどPythonほとんど書いたことない私さのかずや(Twitter:@sanokazuya0306)が理解を深めるためにつくった、TouchDesignerでPython書けるようになりたい人用のメモ。
ほぼ公式の英語チュートリアルの翻訳。
ていうか全部書き終わってからお触り本の後ろの方にまとまっている事に気づいてしまいました。もっと知りたい人はこっち買って!
TouchDesignerでのPythonで得られる幸せ
TouchDesignerは工夫してコード繋げばいい感じの絵を出してくれるけど、変数の扱いとかがめちゃ面倒だったり、コード書く系のジェネやってる人とかはいろいろと不便を感じがち。
そういうときはPython使えると大体解決するっぽい。なので書けるに越したことはなさそう。
公式のチュートリアルもあるのですが、例によって読みにくいので噛み砕きながら日本語で残しておきます。
翻訳シリーズご興味ある方はこちらもどうぞ
TouchDesignerでGLSL入門
ちなみに
この基礎編では、下記公式のチュートリアルを順を追って日本語で紹介していきます。
Introduction to Python Tutorial - TouchDesigner 088 Wiki
ただ正直、公式のチュートリアルがあんまイケてないのでとりあえずTouchDesigner上でPython動かしてみようぜーくらいで、大した内容がありません。
実際どう使ったらエエねん!というのは応用編で紹介します。あらかじめ!
Python入力
公式チュートリアルのInputting Pythonの部分。
TextportでPythonする
TouchDesignerでPythonを試す一番簡単な方法はTextportを使う方法。
メニュー → Dialogs → Textport and DATs でTextportを開く。
そうするとこんな感じの画面が出てくる。なんかごちゃごちゃ出てるときは右上のClearを押すと消える。
試しにhelp(absTime)
と入れると、td ModuleというTouchDesignerのどこでも使える変数(ここではabsTime)についての説明が見られる。td ModuleにあるものならabsTimeじゃなくてもなんでもいけるっぽい。
余談
TouchDesigner099で使える言語はPythonとTScriptの2種類ある。TScriptはC++用?のスクリプト言語。ターミナルで使うっぽい入力方式だけどよくわからない。Wikipediaと公式のリファレンス読んで!
TouchDesignerは最初TScriptで作られていたらしい。それがどこかのタイミングでPythonベースに作り変えられ、TScript記述モードはいまも名残で残っている様子。いまの主流はPython。
左上にPythonマークが表示されているときはPythonモード。これをクリックすると下に切り替わる。
これが表示されているときはTScriptモード。物好きなアナタは試してみてはいかがでしょう。今回は使わないのでもう1度押してPythonモードに戻しておく。
DAT ScriptでPythonする
複数行のスクリプトを使うときは、Text DATなどのDATを使う。
とりあえずText DATを適当に置く。右上にPythonマークが出てたらPython入力モード。上の余談と同様にTScriptモードにすることもできる。
DATの中をいじるため、右下のViewer Active Flagを押す。
Viewer Active Flagのオンオフは選択状態でキーボードのAを押すことでも変更できる。
とりあえずこんな感じでいれてみよう。
for i in range(1,5):
print(i)
1以上5未満の範囲でprint(i)を繰り返す、というPython文。
なんで5は含まへんの?と思って調べてみたら、0から数えて5つめまで(4)ということっぽい。詳しくはこのあたりをご参照。
【Python入門】range関数で繰り返し処理をする方法とは? | プロスタ
ちなみに1行目にはスペースやタブを入れてはならず、2行目以降のインデントはタブ1つか半角スペース4つでないといけないらしい。
これをText DATの中にコピペ。
右クリックしてRun Script、もしくは⌘+R。
一見何も起きないが、メニュー→Dialogs→Textports and DATsからさっきのTextportをもう一度開くと…
Textportのなかで実行されていることがわかる。おめでとう。
Moduleをimportする
Pythonを使えることのありがたみの1つは、Pythonで導入できるModuleを使えること。TouchDesignerは最初から、一般的なModuleやNumPyなどの科学計算モジュールなどが入っているらしい。
プログラミング慣れしている方ならおなじみだと思いますが、Moduleとは、みんなが使える「定義や文が入ったファイル」。例えば四則演算以外のややこしめの計算とかも簡単にできたり。詳しくはこのへん。
6. モジュール (module) — Python 3.6.3 ドキュメント
例えばさっきのText DATにこんな感じで入れてみる。
import math
print(math.sin(4))
mathというModuleをimport(使いますよという宣言)して、mathの中のsin計算をするsinという関数(math.sin()と書く)を引っ張って、そこに4をいれてますよ、というプログラム。
そして⌘+R。
sin(4)の計算をしてくれてますね。ちなみに()の中は度ではなくラジアン(4[rad]=4*180/π[度]≒229[度])なのでご注意。
なお、新規Moduleの追加やModuleのバージョン変更は結構面倒くさいっぽいのでここでは省略。TouchDesigner099はPython3.5で動いているっぽく、PreferenceのPython 64-bit Module Path
でPath指定すればいけるっぽいが、試してません(MacのPython Moduleがどこにあるのかわからんかった)。誰か必要なら試して!
DATからInternal Moduleをつくる
これはかなり便利っぽいテク。Paletteで出せるものにもけっこう使われているので知っておいて損はなし。
Text DATを2つつくり、1つの名前をmy_utils
にし、下記を入力。
def my_adder(x,y):
return 10*x+y
もう1つをtest_import
とし、下記を入力。
import my_utils
a = my_utils.my_adder(1,5)
print(a)
上のmy_utils
内のmy_adder(x,y)
は、10*x+yを計算する関数。
それを下でmy_utils.my_adder(x,y)
で呼び出して、x,yを代入、print。
つまり、my_utils
がInternal Moduleとなって、別のDATで活用できる。 便利か〜
Component Module
上記のように同じ階層にあるとInternal Moduleをちゃんと読み込んでくれるが、別の階層にあると読み込んでくれない。
別の階層にあるとこんな感じで怒られる。
しかし1ヶ所、共通でModuleを読み込んでくれる場所がある。
それがlocal/Modules
。
local
の中に入って
Container COMP
を置き、modules
に名前を変えて、ここの中にmy_utils
を置く。ここにあるModuleをComponent Modulesと呼ぶっぽい。
mod (Modules On Demand)
TouchDesignerでしばしば見るmod
について。mod
はimportの欠点を補完するためのもの。
というのも、便利なimportだが、2つの欠点がある。
- Moduleの名前は単語でないといけないため、特定の階層を指定したくても相対パスが使えない。
- import文がウザいため、パラメータ表現に向いていない。
(ごめんなさい、正直なんでパラメータ表現にimport文が邪魔なのかよく分かってません、先生おしえて!)
これらを解決するために、TouchDesignerにはmod
という関数(?)がある。これを使うとimport文が不要になり、相対パスが使えるようになる。どういうことかというと…
a = mod.my_utils.my_adder(1,3)
print(a)
importが必要なModule名の最初にmod
をつけるだけで、上記のように置き換えることができるようになる。
a = mod('my_utils').my_adder(1,3)
print(a)
さらに、(' ')
で囲んでやれば相対パスが指定できる。
a = mod('/project1/my_utils').my_adder(1,3)
print(a)
もちろん、絶対パスも指定できる。 便利か〜
Operatorで操作する
Working with OPs in Pythonの内容。
TouchDesigner独特の関数(?)による指定の仕方に慣れる回。
基本アクセス
よく使うものにme
とroot
がある。
me
: いま計算しているやつ。Operatorの中で使うときはそのOperator自身を指す。root
: ルート階層(一番上)のComponentを指す。
それからop( )
というのを使うと各Operatorの情報にアクセスできる。詳しくはこちら。
OP Class - TouchDesigner 088 Wiki
たとえば、Wave COMPとConstant COMPを出し、Constant COMPのname0
の左にある+を押し、青いボタンを押し、op('wave1')
と入力する。
そうするとこうなる。op('wave1')
はwave1の絶対パスを表示してくれる。
厳密に言うと絶対パスを指定しているわけではなく、wave1というオブジェクトを指定していて、便宜上そのパスを表示しているだけなのだけど、オブジェクト指向の説明はややこしいので省略します。
もちろんこの書き方はDATの中などでも使えて、n = op('wave1')
のように変数に入れたりもできる。
me.parent()
と入れると親階層を指定したりもできる。
COMPであればme
を使って、me.op('子ノードの名前')['チャンネル名']
でCOMP内の子ノードの数値を取ってこれたりもする。
COMPじゃなくても、op('COMPの名前').op('COMPの子ノードの名前')['チャンネル名']
とやれば下の階層の数値を引っ張り出せる。
例
n = op('/project1')
m = n.ops('text*')
for a in m:
print(a.name)
Project
以下のOperatorで、'text*'
に当てはまるものを抽出し、その名前をprintする。
Text DATにこれを入力すると…
print('i am ', me)
print('child of ', me.parent())
print('grandchild of ', me.parent(2))
print('root children:')
k = root.children
for r in k:
print(r)
me
や.parent()
、root
、.children
をそれぞれ使ったもの。
これをいれると…
コマンド早見表
TouchDesigner Operaotr&Python Scriptまとめ
こちらにもすでにある程度まとめられていますが…
公式ページのコマンド/表現一覧は、コマンド一覧はあまりにコマンドライン的操作すぎるので省略。必要そうな表現一覧だけ記載。
目的 | Pythonコード |
---|---|
同じOPのパラメータを取得 | me.par.tx / me.par.value0 |
別のOPのパラメータを取得 | op('sphere1').par.tx / op('constant1').par.value0 |
OP(例:constant1)の出力(chan1)を取得 | op('constant1')['chan1'] |
OPの親 | .parent() |
OPの親の親 / 親×n | .parent(2) / .parent(n) |
table1の1,1を取得 | op('table1')[1,1] |
OPの名前 | .name |
OPの数値 | .digits |
ランダム数値を生成 | tdu.rand(me.digits) ※()内はランダムシード |
要素(container1の子ノード)の数を数えたいとき | len(op('container1').children) |
要素(switch1)のinput/outputの数を数えたいとき | len(op('switch1').inputs) / len(op('switch1').outputs) |
Info CHOPの内容はop().xxで引っ張り出せる | op('moviein1').width |
時間(フレーム単位) | absTime.frame / me.time.frame |
時間(秒単位) | absTime.seconds / me.time.seconds |
などなど…お触り本の一番後ろの方にもあるのでご参照あれ。
以上これまで
以上で公式リファレンスの内容は大体終わりなんですが、これでみなさんPythonマスターですね!!
とはいえ
そう、みなさんお気づきの通り、
結局変数どうやって扱ったらええの?if文とかfor文的なやつとかどうやって書くの?とかが公式リファレンスには全くありません。
公式リファレンスの説明冗長すぎひん?これまでの要素説明するのにこんな解説量いる?
基本自分でやれやというスパルタ感なので、我々プログラミングわからへんからTouchDesignerやってんねん勢にはちょっとまだまだハードル高くて泣ける。
なので具体的にどう書けばifとはforとかできんの?あとCHOPとか使いたいねんけど?というのは応用編でまとめたいと思います。
翌日のアドカレをおたのしみに〜