A Cinder block client for Most Pixels Ever, supporting Mac, Windows and iOS.
Most Pixels Ever is a framework that synchronizes frame-based applications across multiple screens. Read more about the project: https://github.com/shiffman/Most-Pixels-Ever
A video of the sample desktop application: http://www.youtube.com/watch?v=UshNhidsihU
A video of the sample iOS application running on 2 iPads: http://www.youtube.com/watch?v=yY4BJZgIvhc
For step-by-step instructions on creating a MPE app with XCode, read the tutorial:
https://github.com/wdlindmeier/Most-Pixels-Ever-Cinder/wiki/MPE-Setup-Tutorial-for-Cinder
A basic MPE 2.0 server is included with the block. Run like so:
$ python mpe-python-server/mpe_server.py
Here's the basic setup required to integrate an MPE Client with your Cinder app.
You can generate your project from this template using TinderBox.
// Subclass your Cinder app from MPEApp
class MyCinderApp : public App, public MPEApp
{
public:
void setup();
void update();
void draw();
// Override functions found in MPEApp
void mpeReset();
void mpeFrameUpdate(long serverFrameNumber);
void mpeFrameRender(bool isNewFrame);
void mpeMessageReceived(const std::string & message, const int fromClientID);
private:
MPEClientRef mClient;
Rand mRand;
BouncyBall mBall;
}
void MyCinderApp::setup()
{
// By default, the client is be configured using assets/settings.xml.
// The settings filename is changed by overriding MPEApp::mpeSettingsFile().
// Each client needs a unique settings file.
// Pass a pointer to an MPEApp (e.g. MyCinderApp) into the client constructor.
mClient = MPEClient::create(this);
}
void MyCinderApp::update()
{
// Connect the client if we're not already.
// Nothing else needs to happen in update().
// Updating the App state happens in mpeFrameUpdate().
if (!mClient->isConnected() && getElapsedFrames() % 60 == 0)
{
// Attempt to reconnect every 60 frames.
mClient->start();
}
}
void MyCinderApp::draw()
{
// App drawing should happen in mpeFrameRender().
mClient->draw();
}
// functions overridden from MPEApp
void MyCinderApp::mpeReset()
{
// This is called whenever a new client joins the loop.
// All state and history should be cleared as if the app
// was just launched. This will be called by the client
// after it connects to the server.
mRand.seed(1);
// This will create the same "random" position each reset because the
// seed is hardcoded to 1.
ivec2 sizeMaster = mClient->getMasterSize();
mBall.position = vec2(mRand.nextFloat(sizeMaster.x), mRand.nextFloat(sizeMaster.y))
mBall.velocity = vec2(mRand.nextFloat(-5,5), mRand.nextFloat(-5,5));
}
void MyCinderApp::mpeFrameUpdate(long serverFrameNumber)
{
// This is where the app state should be modified.
// The FrameUpdateCallback is called whenever we get a message from the server,
// which may be less frequently than update() or draw() is called.
mBall.update();
}
void MyCinderApp::mpeFrameRender(bool isNewFrame)
{
// The viewport is automatically translated by the client so each
// machine is only drawing a segment of the scene.
gl::clear(Color(0,0,0));
mBall.draw();
}
void MyCinderApp::mpeMessageReceived(const std::string & message, const int fromClientID)
{
// Apps can broadcast data to the other clients. E.g.:
//
// mClient->sendMessage("mouse_pos: 100,200");
ci::app::console() << "Client " << fromClientID
<< " sent broadcast message: " << message << std::endl;
}
CINDER_APP( MyCinderApp, RendererGl )