クリエイティブなC++ライブラリ "Cinder" の紹介

祢次金 佑
エンジニア

2010-11-19 11:35:06

こんにちは、人恋しい季節になってきましたね。
研究開発チームの祢次金(@nejigane)と申します。

本エントリではCinderというクリエイティブなコーディング向けのライブラリについてご紹介したいと思います。

Cinderとは

Cinderとは、画像、音声、動画等を簡単に処理&可視化できる、主にビジュアルデザイン向けの強力なC++ライブラリであり、The Barbarian GroupのAndrew Bell氏が中心となってオープンソースとして開発が進められています。

同様の思想を持つProcessingopenFrameworksによく似ており、C++で簡単に記述できるうえ、Windows、MacOSX、iOS(iPhone/iPad)といった複数のプラットフォームをカバーしています。

細かい機能/特徴の紹介は本家サイトに譲るとして、Cinderを極めるとどのぐらいクリエイティブなモノができあがるのかという例をお見せします。以下の動画は、CinderのTutorialも担当されているRobert Hodgin氏が公開している作品です。(※音が出ます。リンク先ではMade with Processingと書いてありますがCinderによる作品としてギャラリーで紹介されています。)

このようにアーティスティックな用途にパワーを発揮しているCinderですが、研究開発タスク向けにも、例えば、何らかの実験過程/結果で得られるデータの可視化等に役立ちそうです。マウスイベントやキーボードイベントも簡単に拾えますので、単なる可視化だけでなく、インタラクティブ性を持たせることも容易です。

Cinderアプリケーションの書き方

それではCinderの基本的な書き方を見ていきましょう。本エントリでは、Cinderのバージョンは0.8.2、環境はMacOSXでXcodeを使うことを想定します。Windowsを使われる場合でも、ソースの書き方は基本的に変わらないはずですので、Xcodeに依存するところを適宜読み替えてください。また、細かい点は説明を省略していきますので、詳細を知りたい方は本家サイトのリファレンスをご覧ください。

雛形の生成

まずは、Cinderのサイトからダウンロードしてきたパッケージに同梱されるTinderBoxというツールを使ってプロジェクトの雛形を作ります。ここではプロジェクト名を “Test” とします。

ディレクトリと必要最低限のファイルが生成されますので、その中のxcodeprojファイルを開きXcodeを起動しましょう。今回最も重要なのはTestApp.cppというファイルで、デフォルトで以下のようになっているはずです。

#include "cinder/app/AppBasic.h"
#include "cinder/gl/gl.h"

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

class TestApp : public AppBasic {
  public:
	void setup();
	void mouseDown( MouseEvent event );
	void update();
	void draw();
};

void TestApp::setup()
{
}

void TestApp::mouseDown( MouseEvent event )
{
}

void TestApp::update()
{
}

void TestApp::draw()
{
	// clear out the window with black
	gl::clear( Color( 0, 0, 0 ) );
}


CINDER_APP_BASIC( TestApp, RendererGl )

TestAppクラス

この段階でビルドしても、真っ黒なウィンドウが出るだけです。AppBasicのサブクラスであるTestAppクラスに色々実装していく必要があります。

TestAppクラスのメソッドで最も基本的なものは、setup、update、drawの3つです。アプリケーションが実行されると、setupが最初に1度だけ呼ばれ、その後updateとdrawが毎フレーム呼ばれます。基本これだけです。setupでTestAppの状態を初期化し、updateでその状態を更新、drawでその状態をもとにウィンドウに描画を行います。このあたりはopenFrameworksに非常に似ています。

例えば、位置、サイズ固定の円を描きたい場合はdrawに以下のように記述します。

void TestApp::draw()
{
	// clear out the window with black
	gl::clear(Color(0, 0, 0));

	gl::drawSolidCircle(Vec2f(15.0f, 25.0f), 50.0f);
}

これは中心座標(15.0, 25.0)に半径50.0の円を描く例です。Vec2fはCinderで用意されている2次元ベクトルを表すクラスの一つです。

また、画像を描画させたい場合は “cinder/gl/Texture.h” と “cinder/ImageIo.h” をインクルードしつつ、以下のように書きます。

void TestApp::draw()
{
	// clear out the window with black
	gl::clear(Color(0, 0, 0));

	gl::Texture image(loadImage(loadResource("image.jpg")));
	gl::draw(image, getWindowBounds());
}

上記を実行するとローカルリソースのimage.jpgという画像をウィンドウいっぱいに描画することになります。

mouseDownはマウスボタン押下イベントを処理するためのメソッドです。他にも、keyDown、mouseDragといったメソッドをオーバーライドすることで様々なイベントに対し処理を記述することができます。マウス押下イベントを拾い、その時の座標をコンソールに書き出すだけの例を以下に示します。

void TestApp::mouseDown( MouseEvent event )
{
	console() << event.getPos() << endl;
}

サンプル実装

では、サンプルとしてTestAppに処理を実装していきます。マウス押下位置にパーティクルを発生させる、非常に簡素なアプリケーションを作ります。

#include <list>
#include "cinder/app/AppBasic.h"
#include "cinder/gl/gl.h"
#include "Particle.h"

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

class TestApp : public AppBasic {
protected:
	list<Particle> particles;
	bool isPressed;
	Vec2i mouseLocation;

public:
	void setup();
	void mouseDown(MouseEvent event);
	void mouseUp(MouseEvent event);
	void mouseDrag(MouseEvent event);
	void update();
	void draw();
};

void TestApp::setup()
{
	isPressed = false;
}

void TestApp::mouseDown(MouseEvent event)
{
	isPressed = true;
	mouseLocation = event.getPos();
}

void TestApp::mouseUp(MouseEvent event)
{
	isPressed = false;
}

void TestApp::mouseDrag(MouseEvent event)
{
	mouseLocation = event.getPos();
}

void TestApp::update()
{
	for (list<Particle>::iterator iter = particles.begin();
		 iter != particles.end();) {
		iter->update();
		if (iter->isDead())
			iter = particles.erase(iter);
		else
			iter++;
	}

	if (isPressed) {
		for (int i = 0; i < 5; i++)
			particles.push_back(Particle(mouseLocation));
	}
}

void TestApp::draw()
{
	gl::clear(Color(1.0f, 1.0f, 1.0f));

	gl::color(Color(0, 0.7f, 0.7f));
	for (list<Particle>::iterator iter = particles.begin();
		 iter != particles.end();
		 iter++)
		iter->draw();
}

CINDER_APP_BASIC( TestApp, RendererGl )

マウスを押下している間はランダムな方向に飛散する大小様々なパーティクルが発生し、発生したパーティクルは寿命を迎えると消滅します。
TestAppで利用されるParticleクラスの実装は以下の通りです。

#pragma once
#include "cinder/Vector.h"

class Particle {
public:
	Particle(ci::Vec2i location);
	void update();
	void draw();
	bool isDead();

protected:
	ci::Vec2f location;
	ci::Vec2f direction;
	float velocity;
	float radius;
	int life;
};
#include "Particle.h"
#include "cinder/Rand.h"
#include "cinder/gl/gl.h"

using namespace ci;

Particle::Particle(Vec2i _location)
{
	location = _location;
	direction = Rand::randVec2f();
	velocity = Rand::randFloat(8.0f);
	radius = Rand::randFloat(8.0f);
	life = Rand::randInt(50, 250);
}

void Particle::update()
{
	location += direction * velocity;
	life--;
}

void Particle::draw()
{
	gl::drawSolidCircle(location, radius);
}

bool Particle::isDead()
{
	return life <= 0;
}

コンストラクタでは初期位置だけが与えられ、移動方向、速さ、半径、寿命はランダムに初期化されます。以上のソースをビルド&実行し、ウィンドウを適当にマウスでぐりぐりすると以下のようになります。

Hodgin氏の作品とは比べるべくもありませんが、サンプルということでご容赦ください。

おわりに

Cinderでは非常に簡単にインタラクティブでビジュアル的にリッチなアプリケーションをC++で実装することができます。お見せしたサンプルは質素なものでしたが、デザインに素人な私でも上記程度のものなら簡単に実装することができました。本家サイトにはTutorial等のドキュメントもある程度そろっていますので、興味を持たれた方は試してみてはいかかでしょうか。

また、余談ですが現在CinderプロジェクトではCinderISOというmesherの開発が進んでいて、以下のような非常に秀麗なアニメーションが表現できるようになるみたいです。とっても楽しみですね!
(良く見えるのはHodgin氏の表現力そのものに依るところが大きそうですが…)

さて、次回の私のエントリでは、Cinderを使ったiPhoneもしくはiPadで動作するアプリケーションの実装例を披露したいと思います。
それでは!

Leave a Reply