Diary 2012年 / Processingで遊ぼう / Diary/2012-1-1 / Diary/2011-12-15
新年あけましておめでとうございます!
今年もマイペースに頑張りますので、どうぞよろしくお願いいたします。
そうそう、今年もミスドの福袋を買いましたよ。
じゃなくって、今年はプログラム記事もあります。
今回はJBullet(3D物理シミュレーションライブラリ)をProcessingから簡単に呼び出す
PBulletというクラスを作ってみました。
Physics3Dサンプルのページへ
ライブラリ化はまだしてませんが、ひとまずサンプルをどうぞ。
新年ということで紅白カラーで縁起の良さをアピールしています。
使い方は簡単。setupなどで以下のようにクラスを生成します。
PBullet bullet = new PBullet(this);
オブジェクトの追加はこんな感じ。
// 球を追加(位置、半径、質量を渡している)
bullet.add( new RigidSphere(new PVector(0, 0, 0), 20, 2.0f) );
あとはdraw内で
bullet.simulation(frameRate);
bullet.draw();
とすれば物理シミュレーションを行ったオブジェクトを描画できます。
このクラスを使うときは、Processingのスケッチフォルダの libraries に jbullet フォルダを作って、さらにその中に library フォルダを作り、JBulletのサイトのjbullet-20101010.zipにある jbullet.jar と vecmath.jar を入れておきましょう。(ここに固めたものも作っておきました)
ついでに、MQOファイルをロードするクラスを若干拡張してMQOファイルから頂点データをもってきて剛体にするようなこともしてあります。
こんな感じで RigidObject クラスを派生したクラスで独自描画もできますし、PBulletのdrawを使わずに、各オブジェクトのapplyTransを自分で呼んで独自描画をしたりもできます。
# 最初はAB法(ベルレ物理というらしい)を使った自前の物理処理を書いたのですが、
# Bullet使えるならそっちの方が「使えるもの」になりそうだったのでこのとおり公開してみました。
enjoy, it. それでは、また。
Trackback(0)
前回に続いて、Processing Advent Calendar 2011の12/15の記事を書きました。
今回は、レイアウトエディタもどきを作ってみたいと思います。
でも、いったい何をレイアウト(配置)するのか?
ひとことで言うなら、「描画関数をレイアウト」します。
LayoutEditorサンプルのページへ
ソースファイルは2つ。
描画関数を抽出したり、描画リストを管理する LayoutManager.pde と、
マウス位置にあわせて描画関数を描画リストに登録する LayoutEditor.pde です。
まず、setupの中で LayoutManager というクラスを初期化しています。
// 初期化
void setup() {
size(512, 512);
// 描画関数をレイアウトマネージャに登録
layout = new LayoutManager(this, "draw_");
setupGUI();
}
このLayoutManager クラスは、引数として渡された "draw_" という文字列で始まる関数を this (PApplet) の中から見つけてきて記録しています。
// 特定の名前で始まる描画関数を抽出
LayoutManager(Object classObj, String methodPrefix) {
this.classObj = classObj;
this.methodPrefix = methodPrefix;
this.methods = new HashMap<String, Method>();
this.items = new ArrayList<Item>();
// 引数で渡されたクラス内で特定名で直接実装された関数を記録
Method decMethods[] = classObj.getClass().getDeclaredMethods();
for(Method m: decMethods) {
if(m.getName().indexOf(methodPrefix)==0) {
println("LayoutManager: add method : " + m.getName());
methods.put(m.getName(), m);
}
}
}
関数を名前で探すためには、クラス情報にアクセスする必要があります。
そのためにJava のリフレクションという機能を使ってます。
getDeclaredMethods() は、直接宣言している全てのメソッドを返す関数です。つまり、親クラスのメンバは無視してるわけですね。まぁリフレクションの乱用は禁物だと思いますが、なかなか面白いです。[1]
記録した描画関数のテーブル(HashMap)は、LayoutEditorの描画関数選択や描画リスト追加で使用しています。
なお、リフレクションを使うには、最初に以下のような import をしておく必要があります。
import java.lang.reflect.*;
- [1]ちなみに、名前で抽出というのはどうもなぁ、と思ったときには、関数の前に「@〜」と独自の印を付けられるアノテーションという機能を組み合わせるとよいでしょう。ただ、Processingの一見グローバルな位置にあるコードは、実際はProcessingのアプリクラス内部のコードになります。独自アノテーションを定義するには、ライブラリ化やEClipse利用など、工夫が必要かもしれません。
さて、ここまでで描画関数全種類の記録はできました。あとは現在選択している描画関数を、
クリックした位置にあわせて LayoutManager の描画リストへ追加するだけです。
layout.add(currentMethod, mouseX, mouseY);
この currentMethod は関数の名前を持つString型の変数です。
今回は controlP5 というライブラリを使って、ドロップダウンリストやボタンを作っています。
GUIについては、サンプルコード、LayoutEditor.pde の setupGUI や controlEvent 関数を見てみてください。
リフレクションで取得した関数の実行方法は、LayoutManager の draw 関数内にあります。
// 登録した描画コマンドを発行
void draw() {
for(Item it: items) {
pushMatrix();
applyMatrix(it.matrix); // 位置などを設定
try {
it.method.invoke(classObj); // 登録してある描画関数の呼び出し
} catch (Exception e) {
throw new RuntimeException(e);
}
popMatrix();
}
}
it.method.invoke(classObj); が関数を実行している箇所です。
try〜catchの例外処理で挟まないとエラーになるのでご注意を。
今回はよりクレイジーな雰囲気を重視して(?)、リフレクション機能を使って生の関数を登録するようにしていますが、引数やメンバの値をパラメータ設定して渡したり、クラスを使ったりするのもよいと思います。
たとえば、GUI部品レイアウトエディターとか作れそうです。
描画位置については、今回は位置だけなので applyMatrix ではなく translate で十分なのですが、
回転やスケーリングもすぐに対応可能なように Matrix にしてあります。
そうそう、書き忘れていましたが、アプレット上では「Save」ボタンが使用できません。
ローカルで「Save」すると、LayoutEditor で追加した描画リストをProcessingのコードとして出力できます。
Processingはソースコードを書いて簡単に絵的な表現ができるのが楽しい言語ですが、
逆に絵的な入力からソースコードを出すこともできる、と。
最後にオマケとして3Dバージョンも作ってみましたので、参考までにどうぞ。
こちらは controlP5 のP3Dでのテキスト描画に問題があったため、
かわりに SpringGUI という GUI ライブラリを使っていますが、やっていることは同じです。
LayoutEditor3Dサンプルのページへ
Utility.pde は前回の Unproject.pde 内から必要な関数を持ってきただけです。
LayoutEditor3D.pde は LayoutEditor.pde と Unproject.pde をあわせた感じで、
2Dを3D対応にして、カメラ回転や移動ができるようにしました。
LayoutManager.pde の中身は同じです。
えーと、今回はこんなところです。
昨今はソースコードと素材データだけでモノを作る作り方だけではなく、エディターと連携した作り方も増えてきています(といいつつ、実際は昔からそうですけど)。そういったツール作りの参考になればと思います。
工夫すればUnityのような「動かしながらの調整」ができるリアルタイムエディタも作れるかもしれませんね。
# 本当は3Dの方を先に作ったのですが、余計なものが多いので2Dバージョンを作ったという。
Trackback(0)
今、巷では技術系Advent Calendarというのが流行っているらしく、
WritingCafeでもひとつ参加してみることにしました。
まぁ、12月頭からクリスマスまでの間、各言語やテーマに沿った記事を1日1つ、
いろんな人が書いていくお祭りみたいなもの(?)です。
そんなわけでProcessing Advent Calendar 2011の12/2担当記事はこちら。
「Processingでスクリーン座標を3D座標に変換する方法」です。
Unprojectサンプルのページへ
Processingで3D空間の座標(モデル座標、オブジェクト座標)をスクリーン上の2D空間に変換するには、
screenX(x, y, z)、screenY(x, y, z)といった関数を使います。
では、スクリーン座標を3Dのモデル座標に変換するには、どうしたらいいでしょう。
そんなときにあるのがmodelX(x, y)、modelY(x, y)といった関数――だといいのですが、そうではなく。
modelX、modelYは単にローカルなモデル座標をワールド座標に変換してくれるだけです。
なので、あいかわらず3D空間の値しか取れないのです。
OpenGLの関数、gluUnprojectを使えばスクリーンの2D空間の座標を3Dの座標に変換できますが、それだけのためにOpenGL関数やOpenGLレンダラーを使うのはどうも……。
というわけで、今回はこれをProcessingの関数のみで計算してみます。
2D→3D変換の前に、まず3D→2D変換のための行列を作りましょう。
// モデル座標をスクリーン座標に変換する行列を取得
PMatrix3D getModelToScreenMatrix() {
PMatrix3D projection = new PMatrix3D();
matrixMode(PROJECTION); getMatrix(projection);
PMatrix3D modelview = new PMatrix3D();
matrixMode(MODELVIEW); getMatrix(modelview);
PMatrix3D viewport = new PMatrix3D();
viewport.m03 = viewport.m00 = width * 0.5f;
viewport.m13 = viewport.m11 = height * 0.5f;
PMatrix3D m = new PMatrix3D(modelview);
m.preApply(projection);
m.preApply(viewport);
return m;
}
モデルビュー行列やプロジェクション行列の取得方法は、リファレンスには載っていませんが、
matrixModeとgetMatrixを組み合わせると取得可能です。[2]
ちゃんとviewport行列も作っているところがポイント。
- [2]PGraphics3Dのメンバ変数を PGraphics3D p3d = (PGraphics3D)g; 経由で、p3d.modvwとか、p3d.projectionとか、p3d.cameraといった行列を直接参照する方法もあります。
3D→2D変換の行列ができれば、目的の8割は達成したといえます。
// モデル座標をスクリーン座標に変換
PVector projectScreen(PVector v) {
PMatrix3D m = getModelToScreenMatrix();
return matrixCMult(m, v);
}
projectScreen関数内では、getModelToScreenMatrixで取得した3D→2D変換行列を
matrixCMult関数を使ってモデル座標のベクトルを掛けあわせた後、wで割っています。
// 行列で変換したベクトル(wで割ったもの)を返す
PVector matrixCMult(PMatrix3D m, PVector v) {
PVector ov = new PVector();
ov.x = m.m00*v.x + m.m01*v.y + m.m02*v.z + m.m03;
ov.y = m.m10*v.x + m.m11*v.y + m.m12*v.z + m.m13;
ov.z = m.m20*v.x + m.m21*v.y + m.m22*v.z + m.m23;
float w = m.m30*v.x + m.m31*v.y + m.m32*v.z + m.m33;
if(w!=0) ov.mult(1.0f / w);
return ov;
}
中身はともかく、projectScreen関数は、以下のように呼び出せばOK。
PVector modelPos = new PVector(10, 20, 30);
PVector screenPos = projectScreen(modelPos);
screenPos.x、screenPos.y に それぞれ screenX や screenY 関数で計算したときと
ほぼ同じ値が入り、スクリーンに投影できていることが分かります。
これをふまえて、2D→3D変換の関数を作ります。
// スクリーン座標をモデル座標に変換
PVector unprojectScreen(PVector v) {
PMatrix3D m = getModelToScreenMatrix();
m.invert();
return matrixCMult(m, v);
}
違うのは関数名の他に1行だけ。
m.invert();
と、変換行列を逆行列にしているだけですね。
unprojectScreen関数を使うときは、以下のように呼び出せばOK。
PVector mouse3D = unprojectScreen(new PVector(mouseX, mouseY, 0));
さらにunprojectScreenでZ値を1にした座標やカメラ位置と
mouse3Dの差分ベクトルを使ったりすればマウス位置から3D空間に飛ぶ直線が得られます。
詳しいところはサンプルをいじって確認してみましょう。
# 本当は別の少しクレイジーなネタを書くつもりだったのですが、
# そのためにこの変換処理の説明が必要だったので、こちらを記事にしちゃいました。
Trackback(0)
前回作成したMQOモデルファイルの読み込みクラスを、ライブラリ化しました。
使い方は、プログラムの冒頭あたりで
import eyln.mqoloader.*;
とライブラリを import した後、
MQOModel model = new MQOModel(this, "ファイル名");
のようにモデルをロードして[3]
- [3]ファイル名なしのコンストラクタで初期化したあと model.load("ファイル名") でもOK
model.draw();
と描画するだけです。
使ってみたい方は、こちらのMQOLoader.zipをダウンロード後、Processingの作業フォルダ(Sketchbookフォルダ)直下のlibrariesフォルダに展開(解凍)してご使用ください。ライセンスはMIT Lisenceです。
しかし、Javaは不慣れ(というかProcessing以外で触れたことがほとんどない)でライブラリ化に結構手間取りました。公開されている processing-library-template やフォーラム Problems using the library template のおかげでなんとか。(でもライブラリテンプレート自体にバグがあったとは…)
改良Verなどできましたら、ぜひご連絡を:)
Trackback(0)
これ以前のCafe Diary