Hello Cinder

In this post I will walk through the tutorial that the Cinder webpage proposes as an introduction to the language. It is outdated and because of that I had to change few things here and there. On top of that I made some modifications on my own.

This is supposed to be just a little exercise to get accustomed to the library. If you are new to C++, you should get some knowledge about the language first. I recommend www.learncpp.com for starting.

The goal is to produce a simple particle system that will react to the mouse movement, revealing a picture. Let’s see the whole process, from getting the library to the actual code.

 

1. GETTING CINDER

Cinder comes with some pre-compiled packages, however they are not suited for Visual Studio 2017, which I use. Because of that I had the clone the GitHub repository and build it on my own. This was not a problem, I simply followed the specific guide and updated the SDK and the Platform toolset when Visual Studio asked to do so during the build.

 

2. STARTING A CINDER PROJECT

Cinder comes with a nice tool called TinderBox. This allows in few clicks the creations of projects (including Visual Studio) with all the settings necessary to start using Cinder. You find it in Cinder\tools\TinderBox-Win (make a shortcut to your desktop).

TinderBox

When we open a project created with TinderBox, we see that the main functions and classes necessary to work with the library are already set up. The view of setup() and draw() reminded me of Processing so I felt a little bit more at home 🙂 The difference here is that draw() should be used only for rendering, while for updating all our values we will use update(), which is called first. Finally, the macro at the bottom will run our app.

Cinder Project

 

3. CREATING THE PARTICLE SYSTEM

Since the whole process is already described in the Cinder guide, I will just move to the end result.

We have created two classes, Particle and ParticleController. The particles are aligned along a grid in the ParticleController constructor. Here we also load an image that we have previously put in the assets folder of the project and resize it to fit the dimensions of our window.

The meat of the program happens in the update() and draw() function of the Particle class: here we use the mouse position and the time passed since the beginning of the program to give a wavy look to our image that will be represented by the particle system, where each particle has a different radius depending on the gray scale value of the pixels.

Then we put our ParticleController methods inside the setup(), update() and draw() function of our app and we are done, as you can see from the video on the top of the page.

The first impression is good, it is obviously more complicated than Processing but the potential is also higher. After all now we have all the C++ libraries available for us.

This was just a warm-up, I think that now I will try to rewrite the Differential line growth with Processing tutorial in C++ with some changes and see what happens 😉

Here’s the code for this post:

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

#include "ParticleController.h";

using namespace ci;
using namespace ci::app;
using namespace std;

class ParticleSystemsApp : public App {
  public:
  void setup() override;
  void mouseMove( MouseEvent event ) override;
  void update() override;
  void draw() override;

private:
  ParticleController mParticleController;
  vec2 mMouseLoc;
};

void ParticleSystemsApp::setup()
{
  setWindowSize({ 800,600 });
  setFrameRate(60);
  hideCursor();
  mParticleController = ParticleController(getWindowWidth(), getWindowHeight());
}

void ParticleSystemsApp::mouseMove( MouseEvent event )
{
  mMouseLoc = event.getPos();
}

void ParticleSystemsApp::update()
{
  mParticleController.update(mMouseLoc);
}

void ParticleSystemsApp::draw()
{
  gl::clear( Color( 0, 0, 0 ) ); 
  mParticleController.draw();
}

CINDER_APP( ParticleSystemsApp, RendererGl )
#pragma once

#include <vector>

#include "cinder\Channel.h"

#include "Particle.h"

using namespace cinder;

class ParticleController
{
  std::vector<Particle> mParticles;
  Channel32f mChannel;
  float mXRes{ 8 };
  float mYRes{ 8 };

public:
  ParticleController() = default;
  ParticleController(int width, int height);
  
  void addParticles(int xi, int yi);

  void update(vec2& mouseLoc);

  void draw();

};

 

#include "ParticleController.h"

#include "cinder/gl/Texture.h"
#include "cinder\ip\Resize.h"
#include "cinder\Rand.h"


ParticleController::ParticleController(int width, int height)
{
  for (int y = 0; y < height/mYRes; ++y)
    for (int x = 0; x < width/mXRes; ++x)
      addParticles(x, y);
  
  Channel32f src_channel = loadImage("../assets/butterfly.jpg");
  mChannel = Channel32f(width, height);
  ip::resize(src_channel, &mChannel);
}

void ParticleController::addParticles(int xi, int yi)
{
  float x = xi * mXRes;
  float y = yi * mYRes;
  mParticles.push_back(Particle({ x,y }));

}

void ParticleController::update(vec2& mouseLoc)
{
  for (auto&& p : mParticles)
    p.update(mChannel, mouseLoc);
}

void ParticleController::draw()
{
  for (auto&& p : mParticles)
    p.draw();
}

 

#pragma once

#include "cinder\Channel.h"

using namespace ci;

class Particle
{
  vec2 mLoc;
  vec2 mDirToCursor;
  float mRadius{ 4 };
  float mMaxRadius{ 7 };

public:
  Particle(vec2 loc);

  void update(Channel32f& channel, vec2& mouseLoc);

  void draw();
};

 

#include "cinder\app\App.h"
#include "cinder\gl\gl.h"

#include "Particle.h"

Particle::Particle(vec2 loc) : 
  mLoc(loc)
{
}

void Particle::update(Channel32f& channel, vec2& mouseLoc)
{
  mDirToCursor = mouseLoc - mLoc;
  float dist = length(mDirToCursor);
  mDirToCursor = normalize(mDirToCursor);
  float time = app::getElapsedSeconds();
  float sinOffset = sin(dist * 0.005 - time * 0.8) * 50.0f;

  mDirToCursor *= sinOffset;
  
  mRadius = channel.getValue(mLoc) * mMaxRadius;
}

void Particle::draw()
{
  gl::drawSolidCircle(mLoc + mDirToCursor, mRadius);
}

 

Leave a Reply