LoginSignup
27
17

More than 5 years have passed since last update.

TouchDesignerからopenFrameworksアプリを実行する(Windows)

Last updated at Posted at 2018-01-04

はじめに

対象者

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)

td00.jpg

Table DATargumentsと名前を付け、以下のように編集しました。

0 1
0 fullpath oFアプリの実行ファイルの絶対パス

続いて、CHOP Execute DATを以下のように編集します。

chopexec1
# 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

実行結果

gif00.gif

oFアプリ実行時に引数を渡す

oFアプリ実行時に、引数としてウィンドウの横幅、縦幅の値を渡すことで、アプリのウィンドウサイズを設定してみます。
table DATwindowWidth, windowHeightの行を追加し、Chop Execute DAT内を一部修正します。

td01.jpg

0 1
0 fullpath oFアプリの実行ファイルの絶対パス
1 windowWidth oFアプリの横幅
2 windowHeight oFアプリの縦幅
chopexec1
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アプリも一部修正を加えます。

main.cpp
#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main(int argc, char * argv[]){ // 引数 argc, *argv[]を追加
    ofSetupOpenGL(1024,768,OF_WINDOW);
    ofRunApp(new ofApp(argc, argv));

}
ofApp.h
#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

    public:
        ofApp(int argc, char * argv[]); // コンストラクタを追加

        void setup();
        void update();
        void draw();

        // 以下略

};

ofApp.cpp
// 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[]を引数に持つコンストラクタを追加します。
コンストラクタ内で第二、第三コマンドライン引数を取得し、ウィンドウサイズを変更しています。

実行結果

gif01.gif

TouchDesignerから実行中のoFアプリを終了させる

taskkillで実行中のoFアプリを終了

Button COMPがもう一度押されたら、oFアプリを終了させるように、Chop Execute DATを修正します。

chopexec
# 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を指定し、アプリを終了しています。

実行結果

gif02.gif

oFアプリからSpoutでTouchDesignerにテクスチャーを送る

oFアプリ実行時に、TouchDesignerからSpoutのSender Nameを渡す

td03.jpg

まずはプロジェクトにSyphon Spout In TOPを追加。
次にtable DATspoutSenderNameの行を追加し、任意の名前を入力します。

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 DATonOffToOnを以下のように書き換えます。

chopexec1

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で送信しています。

ofApp.h
#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;
};
ofApp.cpp
#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を終了
}

実行結果

gif04.gif

oFアプリを不可視にする

oFアプリのウィンドウを不可視にする

以下のように、ofMain.cppを修正します。
コマンドライン引数が1つ以上ある場合、ofGLFWWindowSettings.visible = false;とすることで、アプリを不可視にしています。

ofMain.cpp
#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")を追加します。

ofMain.cpp
#include "ofMain.h"
#include "ofApp.h"

#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") // 追加

//========================================================================
int main(int argc, char * argv[]){
    // 中略
}

taskkill時にコンソールを表示させない

CHOP Execute DATonOnToOff内を以下のように修正します。

chopexec1
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

実行結果

gif05.gif

27
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
17