2008/09/12

Flight404のソースコードを読み解く part4


パーティクルを放出させる

旅行から帰ってきましたので、ようやっと更新することができました。まだまだ飽きてはいませんよ。
前回はエミッタを放出させるところまで行ったので、今回はパーティクルを等速で放出させるところまでやっていきます。

今回のソースは以下。相変わらず_gl.pdeは変わっていないので省略。新しくparticle.pdeを加えました。

Particle3.pde(draw()の関数だけ変更されているので、その部分だけ記載。)

void draw(){
background( 0.0 );
perspective( PI/3.0, (float)width/(float)height, 1, 5000 );

gl.glDepthMask(false);
gl.glEnable( GL.GL_BLEND );
gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE );

pgl.beginGL();
emitter.exist();
pgl.endGL();

if( mousePressed ){
emitter.addParticles(20);
}

}

emitter.pde

class Emitter {
Vec3D loc;
Vec3D vel;
Vec3D velToMouse;

color myColor;

ArrayList particles;

Emitter(){
loc = new Vec3D();
vel = new Vec3D();
velToMouse = new Vec3D();

myColor = color( 1, 1, 1 );

particles = new ArrayList();
}

void exist(){
setVelToMouse();
findVelocity();
setPosition();

//rendering Emitter
gl.glEnable( GL.GL_TEXTURE_2D ); //Enable texture mapping
iterateListExist();
render();
gl.glDisable( GL.GL_TEXTURE_2D );
}

void setVelToMouse(){
velToMouse.set( mouseX - loc.x, mouseY - loc.y, 0 );
}

void findVelocity(){
vel.interpolateToSelf( velToMouse, 0.2 );
}

void setPosition(){
loc.addSelf( vel );
}

void iterateListExist(){
pgl.bindTexture( particleImg );

for( Iterator it = particles.iterator(); it.hasNext(); ){
Particle p = (Particle)it.next();
if(!p.ISDEAD){
p.exist();
} else {
it.remove();
}
}
}

void render(){
pgl.bindTexture( emitterImg );
renderImage( loc, 200, myColor, 1.0 );
}

void addParticles( int _amt ){
for( int i=0; i<_amt; i++ ){
particles.add( new Particle( loc, vel) );
}
}
}

particle.pde

class Particle {
int len;
Vec3D[] loc;
Vec3D startLoc;
Vec3D vel;

float radius;
float age;
int lifeSpan;
float agePer;
boolean ISDEAD;

Particle( Vec3D _loc, Vec3D _vel ){
radius = random( 20, 60 );
len = (int)radius;
loc = new Vec3D[len];

startLoc = new Vec3D( _loc.add( new Vec3D().randomVector().scaleSelf( random(5.0) ) ) );

for( int i=0; i
loc[i] = new Vec3D( startLoc );
}

vel = new Vec3D( _vel.scale( 0.5 ).addSelf( new Vec3D().randomVector().scaleSelf( random( 10.0 ) ) ) );

age = 0;
lifeSpan = (int)radius;
agePer = 1.0;
}

void exist(){
setPosition();
render();
setAge();
}

void setPosition(){
for ( int i=len-1; i>0; i-- ){
loc[i].set(loc[i-1]);
}
loc[0].addSelf( vel );
}

void render(){
color c = color( agePer, agePer*0.75, 1.0 - agePer );
renderImage( loc[0], radius*agePer, c, 1.0 );
}

void setAge(){
age++;

if( age > lifeSpan ){
ISDEAD = true;
}else{
agePer = 1.0 - age/(float)lifeSpan;
}
}
}

emitter.pdeの中で特に注目すべきなのは、iterateListExist()の追加。これによって各パーティクルのレンダリング処理を行い、死んだパーティクルをリストから除外している。

particle.pde の中で特によくわかんなかったのが、loc[]の存在について。別に1個だけでいいじゃんと思っていたが、これは後に尻尾というか、パーティクルの軌跡を描画するために使うようだ。loc[0]に一番最新のパーティクルの位置が格納されていて、時が経つごとに順次入れ替えを行っている。

次によくわかんないというか、感覚的じゃないなと思ったのが、new Vec3D( _loc.add( new Vec3D().randomVector().scaleSelf( random(5.0) ) ) )などのベクトル処理。
randomVector() はベクトルをランダムな単位ベクトルに変更するメソッドで、いろいろと便利。scaleSelf()は文字通り、ベクトルを拡大したり縮小したりするメソッド。この返り値を_locベクトルに加算させていることは理解できるのだが、いかんせん感覚的じゃないのが困り者。vec1+vec2とか出来れば理解しやすいのにとか思うけど、javaは演算子のオーバーロードをサポートしていないから仕様がないのか。

4 件のコメント:

Unknown さんのコメント...

particle emitterについて質問なんですが、
実行ボタンを押すととemitter.pde内で

「The method bindTexture(PImage) from type PGraphicsOpenGL is not visible」

というメッセージが出て実行できません。
どう対処すればエミッターを見ることができるようになるのでしょうか???
僕はProcessing(だけでなくプログラミング関連全般)はまだ初心者でよく分からないので、ぜひ教えて頂きたいです。
Processingのバージョンは1.0.1です。

お願いします。

rezoolab さんのコメント...

こんにちは。

こちらも専門家というわけではないので詳しいことは分からないのですが、0.96まで存在していたPGraphicsOpenGLのbindTextureメソッドはProcessing 1.0になってから廃止されたようです。
texture関数が用意されているし、そっちで充分だろうという判断かもしれません(恐らくは…)

Unknown さんのコメント...

なるほど。
そういうことがあったのですか。
ありがとうございました。

あ、そういえばブログでソースコードを載せていらっしゃるときにProcessingのIDEみたいに色分けがされていますよね。
それってご自分で色を付けていらっしゃるのでしょうか?
それともそういう色分けするツールみたいなのがあるのでしょうか?
後者の場合興味があります。

質問ばかりですいません。(汗

Unknown さんのコメント...

色分けの件ですが、google-code-prettifyというのを使えばOKということが分かり解決したので質問を取り消します。すいません。