greenhouse
a creative coding toolkit for spatial interfaces

Getting Started

Run your first application

Once you've downloaded the SDK, let's get you started running the samples.

Write your first application

Getting you set up hacking on a new Greenhouse application.

Spatial Considerations

Greenhouse applications know where they are in the 3D space of the room you're in. Let's talk about that.

Respiration

A dissection of the Greenhouse application's main loop.

Helping XCode Find Assets

How to configure an Xcode project so it finds assets and resource in any directory you choose.

Memory Management

A description of Greenhouse's memory management system

FAQ

Frequently asked questions about Greenhouse.

Handling User Input

Greenhouse supports traditional mouse and keyboard input as well as a variety of new and emerging input modes

Input from the g-speak mobile app

A free iOS/Android app from Oblong lets a mobile device be used as a pseudo-pointer or trackpad for controlling Greenhouse applications

Integrating with a 3d-sensor

Learn how to use Microsoft Kinect for Xbox or OpenNI-Compliant devices as user inputs for a Greenhouse app.

Integrating with the Leap

Got Leap? Here's how to use it as input to your Greenhouse app.

Handle command line arguments

All Greenhouse applications have a global variable called args. You can inspect it at any time in Setup() or afterward. It’s a list (a Trove) which contains all the command line arguments, represented as trimmed strings (Str).

The ‘echo’ sample at /opt/oblong/greenhouse/samples/echo is an example of using command line arguments. We want to let users specify an alternate pool to use. We let them optionally provide a ‘-p’ argument, followed by the name of a pool. It looks like this:

 $ echo -p mypoolname

And it’s handled in echo.C as:

int64 p_pos = args . Find ("-p");
if (p_pos > -1 && args . Count () > p_pos + 1)
  new Echoer (args[p_pos + 1]);

This code looks to see if there is in fact a ‘-p’, and also that there is one argument beyond it – and that one is what we actually want to use.

If the first two command line arguments to a Greenhouse program both end in *.protein, these are taken to be the names of screen and feld proteins (in that order), and they’re used to set up the windows.

Run a Greenhouse application full screen

When they run, Greenhouse programs check for the existence of a file called fullscreen in their current working directory – the directory they are run from.

At the terminal, “touch fullscreen” is an easy way to make an empty file.

Alternately, set an environment variable called FULLSCREEN, e.g.

$ FULLSCREEN=1 ./my-greenhouse-program

You can set environment variables in Xcode in the “Scheme” section.

In code, you can go to fullscreen (or back out again) with a call to SetFullscreenMode().

By default, Greenhouse programs hide the system’s native mouse cursor when they run in full screen mode. This can be controlled by calling SetSystemCursorVisible().

Change properties of the window(s) (size, position, etc.)

The size and position (and orientation) of windows is set up in /etc/oblong/screen.protein and /etc/oblong/feld.protein.

To learn about these settings files, see the Spatial Considerations tutorial.

Run with alternate screen and feld proteins

If the first two command line arguments to a Greenhouse program both end in *.protein, these are taken to be the names of screen and feld proteins (in that order), and they’re used to set up the windows.

Create a windowless Greenhouse app (aka headless app, command line utility, worker process)

It’s common in Greenhouse to create simple background programs that communicate with the main program through protein messages. They might not need to do graphics, or even have a window (aka, a Feld); they just need to do some computation, call a web service, or do something else that the main program would like to offload onto somebody else. We call them worker processes.

You can make a Greenhouse application into a worker by defining the WORKER symbol somewhere before the Greenhouse.h include, like so:

  #define WORKER
  #include "Greenhouse.h"

Or comment that line out again to change back to a regular Greenhouse app.

Check out the hydra sample at /opt/oblong/greenhouse/samples/hydra and /opt/oblong/greenhouse/samples/imago-worker for an example. hydra is a normal Greenhouse app; it has a window. imago-worker is the worker; all it does is look for protein messages sent from hydra, do some work, and send a message back.

Smoothly move an object from one feld to another (on a different plane)

If you are working with multiple machines or multiple monitors, sometimes their feld planes may be at an angle. If we simply move the object’s location (and leave its orientation unchanged), then the object does not “lie flat” on its new feld. So we need to rotate the object to sit flush on the feld.

These two lines will move object o from one feld to another (using animation if animation has been previously applied), and have it assume the orientation of the new feld:

  //  f is the feld it's moving to
  o -> SetTranslation (f -> Loc ());
  o -> RotateLike (f));

We are using the fact that all felds (and all Greenhouse objects) have:

  • Loc, which is their location
  • Norm, which is a normal vector stick out perpendicularly from their front surface
  • Over, which points sideways across their front surface
  • Up, which points upwards across their front surface, perpendicular to the Over vector.

So this kind of approach generalizes nicely; f could have been any Greenhouse object.

Use a Boost library

Boost is a set of free libraries for the C++ programming language that provide data structures, regular expressions, an API for working with the file system, etc.

The Boost libraries are already installed with Greenhouse. To use a Boost library, just include it:

 #include <boost/tuple/tuple.hpp>
 #include "Greenhouse.h"

 void Setup ()
 { boost::tuple<Str, Str, int64> greeting =
      boost::make_tuple ("Hello", "World", 42);
 }

Draw screen-resolution-independent points with a vertex shader

First, we’ll provide our feld resolution (as pixels per millimeter) as input to the vertex shader¬†program.

Vertex shaders are basically one big function, which is run once for each vertex; we want to pass a new argument to this function. So we’ll implement the AssignShaderInputs() method and name the argument (we’ll call it “feld_resolution”), and give it a value (which we get from FeldResolution()):

void AssignShaderInputs ()
  { SetShaderUniform ("feld_resolution", FeldResolution ());
     ....
  }

(This input value is the same for every vertex, so it’s commonly referred to as a “uniform,” probably because it’s . . . uniform.)

Then, in the vertex shader program (e.g. foo.vert), we use it as follows:

  //  This goes before main() somewhere.
  uniform float feld_resolution;

  void main ()
    { .....
      gl_PointSize = your_chosen_size_in_mm * feld_resolution;
      ....
    }

Remove a Node that isn’t a child of something

All Nodes are actually children of a secret global object call the Origin. If a Node hasn’t been designated as a kid of anyone yet, then its Parent is the Origin. You can reach up to the Origin and remove yourself this way:

node -> Parent () -> RemoveKid ();

The Origin’s parent is NULL.