C++ and OpenFrameworks

The first few lectures of term will cover using OpenFrameworks, a framework for creative coding that is similar to Processing but is based on C++.

This means that OpenFrameworks is faster than Processing and is able to access a lot of library functions written in C++. It is often used for computer vision based art works, which are both computationally intensive an use a lot of pre-existing libraries.

However, while being based on C++ makes is more efficient it also makes it harder to work with. Java (which Processing is based on) is designed mostly so that it is easy to program in. If a feature is a bit slower but makes programmers life easier the Java designers will have chosen it (NB for many purposes I think java is still too hard to use and I much prefer easier, but much slower, languages like python). C++ is different, it is designed to be as efficient as possible, and if it means that programming in it is harder, well tough….

C++

There are a lot of good introductions to C++ on the openframeworks site. The most obvious to try is:

Openframeworks for Processing Users

you can also try:

Philip Machanick – C and C++ in 5 days.

and check out others on the openframeworks faq

I will take you through some of the differences between java and C++ (and reasons why C++ is harder).

There are many examples of this but I’ll start with the most basic, how C++ is compiled.

Compiling C++

Computers cannot directly understand programming languages like C++ and Java, the must first be converted into the underlying machine language, a process called compiling.

In C++ this is direct, code is converted directly into machine code:

The result is native machine code which is very fast, but is different between different types of machine. You need to recompile C++ code if you want to run it on a PC or a Mac (OK, my argument doesn’t make sense any more because both are intel based and therefore have the same machine code, but you still have to recompile them, but for other reasons).

Java is completely cross platform, you compile it once and can run it on any machine. That is because it compiles into a java virtual machine code, called bytecode, which is then interpreted at run time into machine code.

This makes life easier but is slower because it is having to translate bytecode to machine code at run time.

There are lots of other ways in which C++ is fast but harder to use, I will cover some in later lectures.

Back to compiling C++.  The picture is more complex.

Firstly, there is something called a pre-processor, which does some text substitution tasks, before compilation. I will discuss it later.

More importantly, programs are made up of multiple files. Each of these are compiled separately, and have to be combined together by a piece of software called a linker to create the final program.

There are two types of file in C++ .cpp files contain code. These are the files that get compiled and eventually linked. However, if they are compiled separately and a class in one cpp file uses a class that is defined in another cpp file, how do they know about each other? This is done through .h files (header files). These contain definitions of classes but no actual code. The .h files are included in the cpp files using the following code:

#include "boid.h"

This is very similar to an import statement in java (or Processing), but works a bit differently. It is done by the pre-processor and works in a really simple way. The preprocessor literally just copies the whole text of boid.h into the cpp file replacing the #include statement.

Openframeworks

Openframeworks is a library of code that provides graphics and sound functionality in much the same way Processing does. Unlike Processing, it does not provide its own development environment, which mean to edit code you will need to you a development environment specific to your operating systems:

Many of the specifics of setting up a project will depend on which you use. I will give an overview of the basic process with examples in xcode. You can get more details from the openframeworks wiki:

OF Startup Guide

You can also use Eleanor Dare’s slides on openframeworks using codeblock.

Open Frameworks

Setting up openframeworks

The first thing you need to do is download openframeworks from their site:

http://www.openframeworks.cc/download

Make sure you download the appropriate distribution for your operating system and development environment. Also make sure you download the FAT version which include addons that we will use.

When you unzip it you will get a folder containing openframeworks, you don’t need to install it.

The directory contains a bunch of examples in the apps->examples folder. Open any of these and you should see a project file for your development environment. For example, on a mac the graphicsExample folder contains a file graphicsExample.xcodeproj. Open this file and it will fire up your development environment. On a mac you will get xcode:

click on the “build and run” button at the top of the screen and xcode will compile the example (will take a bit of time) and then run it. If there are errors compiling it, check the openframework documentation.

The easiest way to start a new project is to just copy an exiting project folder and rename the folder and project file. You can then open it edit it in your development environment. It will give you the basis on which to build your own code.

Another thing you might want to do is to use an addon in your project. Again the easiest way of doing this is to copy an example that uses that addon.

If you want to include an addon in an existing project you need to add the folder of that addon to your project in your developement environment. All the addons are stored in the folder addons in the main openframeworks folder. The openframeworks startup guide for your development environment should explain how to include an addon. I’ll explain it for xcode, the others are similar.

First you need to create a new folder (called a group) for addons. You need to right click (or ctrl click) on the project name at the top of the folder listing on the left of the screen, do new->group:

Then you need to rename that group “addons”:

You then need to add the files to it. Right (or ctrl) click on addons and choose Add->Existing Files

You then find the folder containing the addon you want and add it to the group:

To actually use the addon you will also have to include the header (.h) files from that addon in your files (see below).

Openframeworks code

The openframeworks example apps consist of 2 code (.cpp) files and 1 header (.h) file. If they define extra classes there will be more files, but more on that next week.

I will begin by going through the three files in the example I went through in class.

The first file is Main.cpp. This is the code the starts the app running. Every C++ program has to have a function called main(), which is where the program starts running.

In the case of an openframeworks app main() sets up a window, creates the app and runs it. You generally don’t change this file at all (unless you want to rename your app something different from testApp) so I won’t go through it in detail

#include "ofMain.h"
#include "testApp.h"
#include "ofAppGlutWindow.h"
 
//========================================================================
int main( ){
 
    ofAppGlutWindow window;
	ofSetupOpenGL(&window, 300, 300, OF_WINDOW);			// <-------- setup the GL context
 
	// this kicks off the running of my app
	// can be OF_WINDOW or OF_FULLSCREEN
	// pass in width and height too:
	ofRunApp( new testApp());
 
}

The other two files testApp.h and testApp.cpp define a class.

This class, called testApp, is an extension of the openframeworks application based class ofBaseApp. It allows you to create an application with all the openframeworks functionality but add your own functionality to it. This is exactly what Processing does behind the scenes. When you create a processing sketch you are actually creating a class that extends the PApplet class and draw and setup are methods of that class that you override. So openframeworks is almost the same, though it is all made explicit. We’ll see that the methods of ofBaseApp look very similar to those you are used to in Processing.

As I described above, the class is divided into 2 files. testApp.h gives the definition of the class, i.e. all its methods and variables, while testApp.cpp gives the code contained in the variables.

We’ll start with testApp.h. This defines the class with all its methods and variables. The methods are very similar to those you are used to in Processing, except there is an extra method update(), which is used to separate updating the state of a program (e.g. the position of the bouncy ball) from the drawing code. This make drawing more efficient.

The file starts off with a long description of a particularly strange and broken “feature” of c++.

// All header files start with this strange thing. 
// They are preprocessor definitions which means they 
// are handled by the preprocessor before compiling. 
// To understand why they are needed you need to 
// understand how header files are included. 
// When you include a header file in your code it 
// literally just copies the text into your code. 
// As header files can be included in other header
// files you might get a header file included 
// twice. 
// This would mean that your class gets defined 
// twice, which would break things. 
// This weird bit of code fixes that problem.
// If you ever write your own header files 
// you need to copy these two lines to the top of
// it and change _TEST_APP to something based on 
// the name of your header. 
// you also need to copy the #endif line from 
// the bottom of the file.
#ifndef _TEST_APP
#define _TEST_APP
 
// this part includes header files that define 
// other classes
 
// the main openframeworks header file
// which include all openframeworks classes
#include "ofMain.h"
 
// the vector math extension
#include "ofxVectorMath.h"
 
 
// define the class, it extends ofBaseApp
// the public bit means that the methods of 
// ofBaseApp are also public methods of testApp
class testApp : public ofBaseApp{
 
	// all the methods below here are public
	// i.e. they can be accessed by other classes
	public:
 
		// these are the basic methods of ofBaseApp
		// they are the same as in Processing, except 
		// that there is also an update() method. 
		// Openframeworks separates out updating the
		// state of objects (e.g. changing their position)
		// from drawing them to the screen. 
		// This makes the app run more efficiently
		void setup();
		void update();
		void draw();
 
		// these are methods of ofBaseApp that deal with 
		// input and events
		// again they are very similar to Processing methods
		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void windowResized(int w, int h);
 
		// below here are all the variables associated
		// with the class (called member variables)
 
		// the font used to draw text
		ofTrueTypeFont 	vagRounded;
 
		// you can toggle full screen, this boolean
		// keeps track of whether you are currently
		// full screen
		// NB in c++ the type is called bool
		// not boolean
		bool  	bFullscreen;
 
 
		// we need this variable because their is no equivalent
		// of the mousePressed variable in Processing.
		// As there is already a method called mousePressed
		// we need to call it something different. 
		// I've put an m_ in front of it, short for member. 
		// this is a common convention
		bool m_mousePressed;
 
		// vector variables for the ball's position
		// and velocity
	    ofxVec2f ballPosition;
		ofxVec2f ballVelocity;
 
};
 
// see the comment at the top of the file
#endif

testApp.cpp defines the code of all the methods of testApp.

 
// you have to include the definition 
// of our class
#include "testApp.h"
 
// do the initial setup of the app
// just like in Processing
void testApp::setup(){	 
 
	// set up the size of the window, 
	// similar to the size() command in 
	// processing. 
	// The window size is calculated 
	// based on the size of the whole screen
	int screenW = ofGetScreenWidth();
	int screenH = ofGetScreenHeight();
	ofSetWindowPosition(screenW/2-300/2, screenH/2-300/2);
 
	//load our typeface
	vagRounded.loadFont("vag.ttf", 16);
 
	// we start off not full screen
	// 0 is eqivalent to false
	bFullscreen	= 0;
 
	// set the framerate
	ofSetFrameRate(60);
 
	// set the background colour
	// unlike Processing, this 
	// does't clear the screen, that
	// is done automatically
	ofBackground(50,50,50);	
 
	// set mousePressed to be false
	// you always have to set variables
	// to something
	// unlike java they won't get 
	// set to something sensible
	m_mousePressed = false;
 
	// set the position and 
	// velocity of the balls
	ballPosition.set(150, 150); 
	ballVelocity.set(ofRandom(-5,5), ofRandom(-5,5));
}
 
 
// We use the updated methods to change the position of the
// ball which is later used in draw
void testApp::update(){
 
	// hide the cursor if we are full screen
	if(bFullscreen){
		ofHideCursor();
	}else{
		ofShowCursor();
	}
 
	// air resistance, slightly reduce
	// the velocity every frame
	ballVelocity *= 0.99;
 
 
	// if the mouse is pressed move towards it
	if (m_mousePressed)
	{
		// calculate the vector from the ball
		// to the mouse position
		ofxVec2f difference = ofxVec2f(mouseX,mouseY) - ballPosition;
		// normalise it because we always want to 
		// move at the same speed
		difference.normalize();
		// set the speed at which to move
		// towards the mouse
		difference *= 2.0;
		// add this to the velocity
		ballVelocity += difference;
	}
 
	// add gravity, a constant
	// acceleration downwards
	ballVelocity += ofxVec2f(0, 0.5);
 
	// update the position of the ball
	// by the current velocity
	ballPosition += ballVelocity;
 
	// check for collisions against the edge of the screen.
	// we do it separately for x and y and for each edge 
	// of the screen
	// I also check that current velocity is heading
	// off the screen, if it isn't we don't want to change it
	if (ballPosition.x < 0 && ballVelocity.x < 0){
		// move the ball onto the screen
		ballPosition.x = 0;
		// reverse the direction
		ballVelocity.x *= -1;
	// same for the other edge of the screen
	} else if (ballPosition.x > ofGetWidth() && ballVelocity.x > 0){
		ballPosition.x = ofGetWidth();
		ballVelocity.x *= -1;
	}
 
	// same for y
	if (ballPosition.y < 0 && ballVelocity.y < 0){
		ballPosition.y = 0;
		ballVelocity.y *= -1;
	} else if (ballPosition.y > ofGetHeight() && ballVelocity.y > 0){
		ballPosition.y = ofGetHeight();
		ballVelocity.y *= -1;
	}
 
}
 
// Draws stuff to the screen
void testApp::draw(){
 
	// does important stuff, not sure what
	ofSetupScreen();
 
	// set the drawing color
	ofSetColor(0xFFFFFF);
	// draw a circle at the position of the screen
	ofCircle(ballPosition.x, ballPosition.y, 15);
}
 
// the following methods handle inputs and events 
// most of them do nothing
 
// the f key toggles full screen
void testApp::keyPressed(int key){ 
 
	if(key == 'f'){
 
		bFullscreen = !bFullscreen;
 
		if(!bFullscreen){
			ofSetWindowShape(300,300);
			ofSetFullscreen(false);
			// figure out how to put the window in the center:
			int screenW = ofGetScreenWidth();
			int screenH = ofGetScreenHeight();
			ofSetWindowPosition(screenW/2-300/2, screenH/2-300/2);
		} else if(bFullscreen == 1){
			ofSetFullscreen(true);
		}
	}
}
 
//--------------------------------------------------------------
void testApp::keyReleased(int key){
 
}
 
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
 
}
 
//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
 
}
 
// toggles the m_mousePressed variable that I use 
// in the update methods
void testApp::mousePressed(int x, int y, int button){
	m_mousePressed = true;
}
 
// toggles the m_mousePressed variable that I use 
// in the update methods
void testApp::mouseReleased(int x, int y, int button){
	m_mousePressed = false;
}
 
//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
 
}
Create PDF    Send article as PDF to