Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Eclipse Tutorial

Daniel Shiffman edited this page · 12 revisions

Tutorial on how to use the MPE library with the Eclipse IDE.

If you’ve never used Eclipse before, you can read this Processing in Eclipse tutorial tutorial which covers how to create a Processing sketch in Eclipse. Also, a version control system (such as GIT, subversion, or even Dropbox) is useful when developing the same application across multiple machines. It will allow you easily update the code on several computers, after making changes on just one.

Also, before you try this tutorial, you probably want to read HowThisWorks, as well as ProcessingTutorial. To develop a project using MPE, it's recommended that you start by running the server and your client application locally.

Step 1. Run the server.

Before you can test the client, you'll need to run the server. You have two options for running the server app. Option number one is to grab the source via github, compile and run mpe.server.WallServer.

The second option is to download and extract the MPE server application (download link to come!). The file contains the server application in a jar.

Run the server via the command line.

java -jar mpeServer.jar -verbose -framerate30 -screens2

The server can be configured by changing the command line arguments. Without arguments, the server will launch without logging, at 30fps, and won't wait for any screens before cuing the first connected client to GO!

Step 2. Download the MPE library

Again, you have two options. (1) You can grab the source as an Eclipse project and include it in the build path of your project.

Or (2) you can download the library jar (download link to come!) and include mpe.jar in your build path.

Step 3. Convert a single-screen application to a multi-screen one!

Let's say you have a simple ball bouncing application (http://www.shiffman.net/p5/mpefiles/ballbouncer/) that runs at 320x240. We now want to make it run at 640x240 across two windows. (For testing, it's easiest to run two windows on one machine.)

First, import the library:

import mpe.client.*;

Then create a client object:

// A client object
TCPClient client;

In setup(), initialize the Client object. Here, a settings XML file is required. This file tells the client what its local dimensions are, as well as its location within the master dimensions. <!-- xxx --> indicates a comment and end all lines with a semi-colon (and no spaces!) Here is an example:

<settings>
        <!--ID of client-->
    <id>0</id>
    <name>renderer0</name>  <!-- optional -->
        <!--Server address, localhost for testing-->
    <server>
        <ip>localhost</ip>
        <port>9002</port> 
    </server>
        <!--Dimensions of this screen-->
    <local_dimensions>
        <width>200</width>
        <height>400</height>
    </local_dimensions>
        <!--Location of this screen (to-left corner)-->
    <local_location>
        <x>0</x>
        <y>0</y>
    </local_location>
        <!--Dimensions of entire screen-->
    <master_dimensions>
        <width>400</width>
        <height>400</height>
    </master_dimensions>
        <!--Set to true to automatically line up screens next to each other-->
    <offset_window>true</offset_window>
        <!--Set to true to see console printing.-->
    <verbose>false</verbose>
        <!--Set to true to run multiple screens without server.-->
    <simulation fps="30">false</simulation>
</settings>

Once you've created the XML file, you can make the client object in setup():

  // make a new Client using an XML file
  client = new TCPClient(this, "mpe.xml");

Once you've made the Client object, you should ask it for the size of your sketch.

  // the size is determined by the client's local width and height
  size(client.getLWidth(), client.getLHeight());

Below setup() define a resetEvent(). Every time a new client connects to the server, all rendering clients will reset.

public void resetEvent(Client c) {
   // Put everything you would normally initialize in setup() here.
   // e.g. x = 0;
   // e.g. y = 0;
   // Call resetEvent(client) from inside setup();
}

Back in setup(), you must make sure to call the resetEvent() and tell the client to start(). It's generally best to do this as the last thing in setup.

// Important, call resetEvent()
resetEvent(client)
// Important, must start the client!
client.start();

Processing's draw() loop is replaced by the frameEvent() callback. This function is triggered by the Client object whenever the server alerts the client that a new frame should be rendered. You must implement this function for the framework to function properly.

//--------------------------------------
// Triggered by the client whenever a new frame should be rendered.
// All synchronized drawing should be done here when in auto mode.
public void frameEvent(TCPClient c) {
  background(255);

  // move and draw all the balls
  for (int i = 0; i < balls.size(); i++) {
    Ball ball = (Ball)balls.get(i);
    ball.calc();
    ball.draw();
  }
}

Draw you can keep empty.

//--------------------------------------
// Keep the motor running... draw() needs to be added in auto mode, even if
// it is empty to keep things rolling.
public void draw() {
}

Note that if you are using the variables width or height in your sketch, you may need to change these to client.getMWidth();

if (x < 0 || x > client.getMWidth()) xdir *= -1;  // Note the use of the master width!

In addition, if you are using any random numbers or Perlin noise, you must make sure to seed them with a constant so that all client screens will pick the same sequence of randoms.

randomSeed(1); // Seed random so all clients pick same sequence
noiseSeed(1);

Broadcasting Messages

You can also broadcast a message from one client to all clients. For example, if you want a mouse connected to one client to act as a mouse for the entire system, you will need to broadcast a message when the mouse is clicked.

//--------------------------------------
// Adds a Ball to the stage at the position of the mouse click.
public void mousePressed() {
  // never include a ":" when broadcasting your message
  int x = mouseX + client.getXoffset();
  int y = mouseY + client.getYoffset();
  client.broadcast(x + "," + y);
}

Note the use of getXoffset() and getYoffset() to account for the mouse’s position within the master dimensions.

The message is then received during a frameEvent(). Note that the message comes in as an array of Strings (the server accumulates these messages into an array.)

  // read any incoming messages
  if (c.messageAvailable()) {
    String[] msg = c.getDataMessage();
    String[] xy = msg[0].split(",");
    float x = Integer.parseInt(xy[0]);
    float y = Integer.parseInt(xy[1]);
    balls.add(new Ball(x, y));
  }

Optionally, you can define a separate dataEvent() to receive messages from the server there instead of inside frameEvent().

public void dataEvent() {
    String[] msg = c.getDataMessage();
    String[] xy = msg[0].split(",");
    float x = Integer.parseInt(xy[0]);
    float y = Integer.parseInt(xy[1]);
    balls.add(new Ball(x, y));
}

Step 4. Ok, how the heck do I run this thing?

Going back to the bouncing ball example, we want to have two windows 320x240 each, together making a bigger window 640x240. The server should have masterDimensions=640x240 and each client will have local dimensions of 320x240.

You should now go ahead and export your sketch to application, and make a copy of that application. You should then have two applications and two XML files. These would ultimately live on separate computers, but for now you can place them in separate folders. Note the application is identical, only the XML file is different!

One XML file would look like this:

<settings>
    <id>0</id>
    <name>renderer0</name>  <!-- optional -->
    <server>
        <ip>localhost</ip>
        <port>9002</port> 
    </server>
    <local_dimensions>
        <width>320</width>
        <height>240</height>
    </local_dimensions>
    <local_location>
        <x>0</x>
        <y>0</y>
    </local_location>
    <master_dimensions>
        <width>640</width>
        <height>240</height>
    </master_dimensions>
    <offset_window>true</offset_window>
    <verbose>false</verbose>
    <simulation fps="30">false</simulation>
</settings>

And the other like this:

<settings>
        <!-- Remember to increment the id of the client -->
    <id>1</id>
    <name>renderer1</name>  <!-- optional -->
    <server>
        <ip>localhost</ip>
        <port>9002</port> 
    </server>
    <local_dimensions>
        <width>320</width>
        <height>240</height>
    </local_dimensions>
        <!-- Note the 2nd client starts exactly 1 screen's width over to the right -->
    <local_location>
        <x>320</x>
        <y>0</y>
    </local_location>
    <master_dimensions>
        <width>640</width>
        <height>240</height>
    </master_dimensions>
    <offset_window>true</offset_window>
    <verbose>false</verbose>
    <simulation fps="30">false</simulation>
</settings>

Then add a global variable to your application to keep track of the client ID number:

final int ID = 0;

Use that ID number to load the correct XML file:

client = new TCPClient(this, "mpe"+ID+".xml");

Execute the application once, then switch ID = 0 to ID = 1 and execute again!

The full example is available with the source.

Something went wrong with that request. Please try again.