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

はじめに

対象者

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


『 Python 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。