はじめに
対象者
openFrameworksでのプログラミング経験者で、TouchDesignerとoFを連携させたい方
サンプルファイル
こちらからダウンロードしてください。
oFアプリについては、バイナリとソースコード、addons.makeファイルのみ入っています。
ソースを編集・ビルドする場合は、TD_oF_samplesフォルダをoFのappsフォルダ直下に配置し、Project Generatorでプロジェクトファイルを作成してください。
実行環境
- Windows 10 Home Version 1709
- TouchDesigner 099 COMMERCIAL 64-Bit Build 2017.14620
- oprnFrameworks 0.9.8 VS Release
- Spout v2.006
TouchDesignerからoFで作成されたアプリを起動する
subprocessでoFアプリを起動
まずは、Button COMP
が押されたらCHOP Execute DAT
のpythonスクリプトが実行され、Table DAT
に書かれたfullpath
の行を参照し、subprocess.Popen
でoFアプリを起動するようにネットワークを構築します。
(参考 : https://docs.python.jp/3/library/subprocess.html)
Table DAT
にarguments
と名前を付け、以下のように編集しました。
0 | 1 | |
---|---|---|
0 | fullpath | oFアプリの実行ファイルの絶対パス |
続いて、CHOP Execute DAT
を以下のように編集します。
# subprocessモジュールを読み込み
import subprocess
def onOffToOn(channel, sampleIndex, val, prev):
# table DATを取得
table = op('arguments')
# アプリの絶対パスを取得
fullpath = table['fullpath', 1].val
# バックスラッシュをエスケープ付きのバックスラッシュに置換
newPath = fullpath.replace('\\', '\\\\')
# popenでoFアプリを起動
proc = subprocess.Popen(newPath)
return
実行結果
oFアプリ実行時に引数を渡す
oFアプリ実行時に、引数としてウィンドウの横幅、縦幅の値を渡すことで、アプリのウィンドウサイズを設定してみます。
table DAT
にwindowWidth
, windowHeight
の行を追加し、Chop Execute DAT
内を一部修正します。
0 | 1 | |
---|---|---|
0 | fullpath | oFアプリの実行ファイルの絶対パス |
1 | windowWidth | oFアプリの横幅 |
2 | windowHeight | oFアプリの縦幅 |
import subprocess
# 中略
def onOffToOn(channel, sampleIndex, val, prev):
# table DATを取得
table = op('arguments')
# アプリの絶対パスを取得
fullpath = table['fullpath', 1].val
# バックスラッシュをエスケープ付きのバックスラッシュに置換
newPath = fullpath.replace('\\', '\\\\')
# windowWidthを取得
windowWidth = table['windowWidth', 1].val
# windowHeightを取得
windowHeight = table['windowHeight', 1].val
# 引数をひとつの配列にまとめる
args = [newPath, windowWidth, windowHeight]
# popenでoFアプリを起動
proc = subprocess.Popen(args)
return
oFアプリも一部修正を加えます。
#include "ofMain.h"
#include "ofApp.h"
//========================================================================
int main(int argc, char * argv[]){ // 引数 argc, *argv[]を追加
ofSetupOpenGL(1024,768,OF_WINDOW);
ofRunApp(new ofApp(argc, argv));
}
#pragma once
#include "ofMain.h"
class ofApp : public ofBaseApp{
public:
ofApp(int argc, char * argv[]); // コンストラクタを追加
void setup();
void update();
void draw();
// 以下略
};
// ofApp.cpp内に追加
ofApp::ofApp(int argc, char * argv[]) {
if (argc == 3) { // 引数が3つの場合
int windowWidth = ofToInt(argv[1]); // 第二引数(windowWidth)を取得
int windowHeight = ofToInt(argv[2]); // 第三引数(windowHeight)を取得
ofSetWindowShape(windowWidth, windowHeight); // ウィンドウサイズを変更
}
}
main.cpp
の引数argc
でコマンドライン引数の個数、*argv[]
でコマンドライン引数自体を取得することが可能です。
(参考 : https://msdn.microsoft.com/ja-jp/library/17w5ykft.aspx)
ofApp
内にargc
, argv[]
を引数に持つコンストラクタを追加します。
コンストラクタ内で第二、第三コマンドライン引数を取得し、ウィンドウサイズを変更しています。
実行結果
#TouchDesignerから実行中のoFアプリを終了させる
taskkillで実行中のoFアプリを終了
Button COMP
がもう一度押されたら、oFアプリを終了させるように、Chop Execute DAT
を修正します。
# subprocessモジュールを読み込み
import subprocess
# process IDを格納する変数
PID = -1
def onOffToOn(channel, sampleIndex, val, prev):
# table DATを取得
table = op('arguments')
# アプリの絶対パスを取得
fullpath = table['fullpath', 1].val
# バックスラッシュをエスケープ付きのバックスラッシュに置換
newPath = fullpath.replace('\\', '\\\\')
# windowWidthを取得
windowWidth = table['windowWidth', 1].val
# windowHeightを取得
windowHeight = table['windowHeight', 1].val
# 引数をひとつの配列にまとめる
args = [newPath, windowWidth, windowHeight]
# popenでoFアプリを起動
proc = subprocess.Popen(args)
# 起動したoFアプリのprocess IDを格納
global PID
PID = proc.pid
return
def onOnToOff(channel, sampleIndex, val, prev):
# oFアプリ起動時に保存したprocess IDを取得し、taskkillでoFアプリを終了させる
global PID
subprocess.Popen("taskkill /pid " + str(PID) + " /f")
PID = -1
return
Button COMP
を押してoFアプリを起動する際、アプリのProcess IDを取得しグローバル変数に保存します。
もう一度Button COMP
を押した際にtaskkill
で先ほど保存したProcess IDを指定し、アプリを終了しています。
実行結果
oFアプリからSpoutでTouchDesignerにテクスチャーを送る
oFアプリ実行時に、TouchDesignerからSpoutのSender Nameを渡す
まずはプロジェクトにSyphon Spout In TOP
を追加。
次にtable DAT
にspoutSenderName
の行を追加し、任意の名前を入力します。
0 | 1 | |
---|---|---|
0 | fullpath | oFアプリの実行ファイルの絶対パス |
1 | windowWidth | oFアプリの横幅 |
2 | windowHeight | oFアプリの縦幅 |
3 | spoutSenderName | SpoutのSender名 |
Syphon Spout In TOP
のsendernameにop('arguments')['spoutSenderName', 1].val
と入力し、
CHOP Execute DAT
のonOffToOn
を以下のように書き換えます。
def onOffToOn(channel, sampleIndex, val, prev):
# table DATを取得
table = op('arguments')
# アプリの絶対パスを取得
fullpath = table['fullpath', 1].val
# バックスラッシュをエスケープ付きのバックスラッシュに置換
newPath = fullpath.replace('\\', '\\\\')
# windowWidthを取得
windowWidth = table['windowWidth', 1].val
# windowHeightを取得
windowHeight = table['windowHeight', 1].val
# spoutSenderNameを取得
spoutSenderName = table['spoutSenderName', 1].val
# 引数をひとつの配列にまとめる
args = [newPath, windowWidth, windowHeight, spoutSenderName]
# popenでoFアプリを起動
proc = subprocess.Popen(args)
# 起動したoFアプリのprocess IDを格納
global PID
PID = proc.pid
return
これでコマンドライン引数として、spoutSenderName
をoF側に送れるようになりました。
oFアプリからSpoutでテクスチャーを共有
ofxSpout2というaddonを使用します。
https://github.com/ThomasMSondrup/ofxSpout2
spoutSenderName
をコマンドライン引数として受け取り、string変数に格納。
spout.sendTexture()
関数で、毎フレーム更新したfboを、TouchDesignerから指定したspoutSenderNameで送信しています。
#pragma once
#include "ofMain.h"
#include "ofxSpout2Sender.h"
class ofApp : public ofBaseApp{
public:
ofApp(int argc, char * argv[]);
void setup();
void update();
void draw();
void exit();
ofxSpout2::Sender spout;
ofFbo fbo;
string spoutSenderName = "";
int windowWidth = 1024;
int windowHeight = 768;
};
#include "ofApp.h"
//--------------------------------------------------------------
ofApp::ofApp(int argc, char * argv[]) {
if (argc == 4) {
windowWidth = ofToInt(argv[1]); // 第二引数(windowWidth)を取得
windowHeight = ofToInt(argv[2]); // 第三引数(windowHeight)を取得
spoutSenderName = argv[3]; // 第四引数(spoutSenderName)を取得
ofSetWindowShape(windowWidth, windowHeight); // ウィンドウサイズを変更
}
}
//--------------------------------------------------------------
void ofApp::setup(){
fbo.allocate(windowWidth, windowHeight); // Fboを作成
}
//--------------------------------------------------------------
void ofApp::draw(){
fbo.begin();
ofClear(0);
// アニメーションを描画
fbo.end();
fbo.draw(0, 0);
// spoutでFboのテクスチャーを送信
if (spoutSenderName != "") {
spout.sendTexture(fbo.getTexture(), spoutSenderName);
}
}
//--------------------------------------------------------------
void ofApp::exit() {
spout.exit(); // ofxSpout2を終了
}
実行結果
oFアプリを不可視にする
oFアプリのウィンドウを不可視にする
以下のように、ofMain.cpp
を修正します。
コマンドライン引数が1つ以上ある場合、ofGLFWWindowSettings.visible = false;
とすることで、アプリを不可視にしています。
#include "ofMain.h"
#include "ofApp.h"
//========================================================================
int main(int argc, char * argv[]){
ofGLFWWindowSettings settings;
settings.width = 1024;
settings.height = 768;
if (argc > 1) {
settings.visible = false;
}
ofCreateWindow(settings);
ofRunApp(new ofApp(argc, argv));
}
oFアプリのコンソールを非表示にする
ofMain.cpp
内に#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
を追加します。
#include "ofMain.h"
#include "ofApp.h"
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") // 追加
//========================================================================
int main(int argc, char * argv[]){
// 中略
}
taskkill時にコンソールを表示させない
CHOP Execute DAT
のonOnToOff
内を以下のように修正します。
def onOnToOff(channel, sampleIndex, val, prev):
# subprocess.STARTUPINFOを設定
info = subprocess.STARTUPINFO()
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
info.wShowWindow = subprocess.SW_HIDE
# oFアプリ起動時に保存したprocess IDを取得し、taskkillでoFアプリを終了させる
global PID
subprocess.Popen("taskkill /pid " + str(PID) + " /f", startupinfo=info)
PID = -1
return