Processing Tutorial - shiffman/Most-Pixels-Ever-Processing GitHub Wiki

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

Before you try this tutorial, you probably want to read How-This-Works.

Step 1. Run the server.

Download and extract the MPE server application.

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 and install the MPE library

Use the add library function in processing to find and install the Most-Pixels-Ever library. Alternatively, you can download the library from here

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

Let's say you have a simple ball bouncing applet 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. 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(TCPClient 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.
 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.
 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.
 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>

You can download the example sketch from the downloads (download link to come)

⚠️ **GitHub.com Fallback** ⚠️