openFrameworks

From TouchDesigner 099 Wiki


TouchDesigner has an infrastructure to process and render openFrameworks projects within the TouchDesigner process via a CPlusPlus TOP.

The TouchDesigner 099 sample project for openFrameworks has been upgraded to the 64-bit openFrameworks v0.9.8, with four examples of a C++ TOP.

Download Examples here:
File:OpenFrameworksExamples.zip

Setup

1) Download openFrameworks and unzip it to your working directory.

2) Copy the following folder in the TouchDesigner install folder:

   C:/Program Files/Derivative/TouchDesigner099/Samples/CPlusPlus/OpenGLTOP

to the following directory in the unzipped openFrameworks folder:

   apps/myApps

Rename the folder to your project's name.

Note: This is not strictly necessary, but it makes it a lot easier because openFrameworks includes a property sheet that lists all the "Additional Include Directories" relative to the root of the openFrameworks folder. Otherwise all the include directories would have to be added manually.

3) Open the Visual Studio Solution (.sln) from the folder you just copied over. You must use Visual Studio 2012 or higher to be able to compile openFrameworks.

4) In Visual Studio, right click on the solution in the "Solution Explorer" and click "Add > Existing Project".

5) Locate the folder where openFrameworks was unzipped and select:

   libs/openFrameworksCompiled/project/vs/openframeworksLib.vcproj

6) Right click on the CPlusPlusTOPExample project and click "Project Dependencies". Check the box next to openframeworksLib in the Dependencies page. Click OK.

7) Right click on the CPlusPlusTOPExample project and click References. Click "Add New Reference" and check the box next to openframeworksLib. Click OK.

8) Right click on the CPlusPlusTOPExample project and click Properties. Find "Configuration Properties > General" and set "Output Directory" to $(SolutionDir)\bin

9) Click "View > Property Manager". Expand the CPlusPlus TOP project. For each of the debug folders, right click and click "Add Existing Property Sheet". Locate the folder where openFrameworks was unzipped and select:

   libs/openFrameworksCompiled/project/vs/openFrameworksDebug.props

10) Do the same for the release folders, except instead select:

   libs/openFrameworksCompiled/project/vs/openFrameworksRelease.props

11) Go back to the "Solution Explorer" and open TOP_CPlusPlusBase.h. Remove the following lines:

   #include <windows.h>
   #include <cstdio>

and replace them with:

   #include "ofMain.h"

12) In CPlusPlusTOPExample.h, add

   #include "ofAppNoWindow.h"

and add the members

   ofAppNoWindow myWindow;
   ofGLProgrammableRenderer *renderer;

to the class body for the TOP.

13) In CPlusPlusTOPExample.cpp: Add the following lines to the constructor method CPlusPlusTOPExample::CPlusPlusTOPExample:

   renderer = new ofGLProgrammableRenderer(&myWindow);
   ofSetDataPathRoot(relative_path_to_new_dir);

where xRes and yRes are the resolution width and height respectively. Because the application being run is TouchDesigner, openFrameworks will search the installed TouchDesigner's bin for /data by default. If you have any shaders/images to load then ofSetDataPathRoot(relative_path_to_new_dir); is necessary to give openFrameworks the path to the /data directory.
Note: relative_path_to_new_dir is a relative path from the executable (in this case the installed TouchDesigner, which should be located under Program Files) to the desired /data directory.
Example: ofSetDataPathRoot("../../../../of_v0.9.3_vs_release/apps/myApps/CPlusPlusTOPExample/bin/data/")

14) Create a CPlusPlusTOPExample::setup function to be called only once:

 void CPlusPlusTOPExample::setup() 
 {
   glewInit();
   renderer->setup(3, 2);
// load any shaders, images, etc }

In addition to glewInit(); and renderer->setup(3, 2); this is where you should load any shaders or images.

15) At the top of CPlusPlusTOPExample::execute query any custom parameters.

16) Query for the resolution width and height:

 int width = outputFormat->width;
 int height = outputFormat->height;

Then setup the window with the width and height of the C++TOP:

 ofSetupOpenGL(&myWindow, width, height, OF_WINDOW);

17) Enclose the rest of CPlusPlusTOPExample::execute with:

 context->beginGLCommands();
// ... rest of your code
context->endGLCommands();

18) Add CPlusPlusTOPExample::setup call enclosed by context

19) For any rendering in openFrameworks it's necessary to enclose it with:

 renderer->startRender();
 renderer->setupScreen();
// any binding of shaders/images, drawing, etc.
renderer->finishRender();

Example CPlusPlusTOPExample::execute after steps 15-19

 void CPlusPlusTOPExample::execute(const TOP_OutputFormatSpecs* outputFormat, OP_Inputs* inputs, TOP_Context *context) 
 {
   // query custom parameters 
int width = outputFormat->width; int height = outputFormat->height;
ofSetupOpenGL(&myWindow, width, height, OF_WINDOW);
context->beginGLCommands();
if(!isSetup) { setup(); }
renderer->startRender(); renderer->setupScreen();
// any binding of shaders/images, drawing, etc.
renderer->finishRender();
context->endGLCommands(); }


19) Build the project. All the DLLs required to use the CPlusPlus TOP are in the following directory in the folder where openFrameworks was unzipped:

   apps/myApps/<project-name>/bin

It is important that all the DLLs remain together in the same directory because of dependencies. TouchDesigner will automatically load the other required DLLs if they are in the same directory.

Translating openFrameworks code to work within TouchDesigner

The creation of an ofAppNoWindow using the above steps will cause many openFrameworks functions to break down and ofGetCurrentRenderer(), which queries the renderer of the window, is to blame for this.
The renderer holds much of the useful renderering functionality, however ofAppNoWindow has an ofNoopRenderer which is missing crucial functionality. As a result ofGetCurrentRenderer() returns an unusable renderer that will either lead to a crash or do nothing.
In order to render in TouchDesigner using openFrameworks ofGetCurrentRenderer() must be bypassed, meaning any function that calls ofGetCurrentRenderer() cannot be used. This is exactly why an ofGLProgrammableRenderer object is created in steps 12-13.
To translate any openFrameworks function that calls ofGetCurrentRenderer(), look at the source and see how it uses the renderer, then move that functionality to your CPlusPlusTOPExample using the created ofGLProgrammableRenderer object.
openFrameworks is open source so you will need to look at the source code to see specifically what functions need to be changed. All drawing, binding, and transforming will need to be changed.

Example of binding a shader:

 shader.begin();

becomes

 renderer->bind(shader);

Using Input TOPs

The following code is an example of how to use input TOPs with openFrameworks:

void
CPlusPlusTOPExample::execute(const TOP_OutputFormatSpecs* outputFormat,
				const TOP_InputArrays* arrays,
				void* reserved)
{
	// ...
	// Use the first input TOP (here we assume it exists but in reality it might not)
	const TOP_TOPInput *topInput = &arrays->TOPInputs[0];
	ofTexture texture;
	texture.setUseExternalTextureID(topInput->textureIndex);
	texture.texData.width = topInput->width;
	texture.texData.height = topInput->height;
	texture.texData.tex_w = topInput->width;
	
	texture.texData.tex_h = topInput->height;
	texture.texData.textureTarget = topInput->textureType;
	// ...
}