WOW-Engine を使うには

WOW-Engine を使うと 3D 物理演算を行い、オブジェクトが衝突する様子をリアルに表現することができます。(Papervision3D 逆引きシリーズ)

WOW-Engine を使うには以下の手順を踏みます。

1, WOWEngine を作成する
物理演算を行う WOWEngine クラスのインスタンスを作成します。
2, WBoundArea を作成する
WBoundArea クラスのインスタンスを作成して setBoundArea メソッドで WOWEngine のバウンドエリアに指定します。物理演算はバウンドエリアの中にあるオブジェクトに対し行われます。
3, パーティクルを作成する
物理演算の対象となるオブジェクトをパーティクルと呼び、WParticleの派生クラスで表現されます。WOW-Engine の機能を一言で表現すると『パーティクルがどのように動くのかを計算し結果の座標を得る』となります。*1パーティクルには WSphere と WPlane の2種類があります。今回は WSphere クラスのインスタンスを作成し addParticle メソッドで WOWEngine に追加しています。

以上手順を踏むことで WOW-Engine 内にパーティクルが配置され物理演算を行う準備が整います。しかし WOW-Engine にはパーティクルを描画する機能はありませんので、オブジェクトを表示するためには別に Papervision3D の 3D オブジェクトを用意する必要があります。パーティクルはあくまで座標計算を行うためにあり、WOW-Engine によって算出されたパーティクルの座標を利用して表示用の 3D オブジェクトをリアルに動かします。

画面上では単一の物体として表現されるのに、物理演算用のパーティクルと表示用の 3D オブジェクトという2種類のクラスを利用しなければならないのは面倒に感じるかも知れません。しかし、物理演算と描画の機能が分離されているので WOW-Engine の演算結果を表示する時 Papervision 以外の 3D 描画ライブラリでも自由に使用できるというメリットがあります。また、表示の上では複雑な形状のオブジェクトでも、物理演算の時は球など単純な形状とみなすことで計算にかかるコストを減らすことができます。

今回は Papervision3D を利用して WOWEngine の物理演算の結果を 3D 表示してみましょう。

4, Papervision3D のオブジェクトを作成する。
ビューと、バンウンドエリア・パーティクルに相当する 3D オブジェクトを用意します。ビューはドキュメントクラスに追加し、startRendering() でレンダリングを開始します。3D オブジェクトはビューが保持するシーンに追加します。
5, 毎フレームごとに演算を行う
毎フレームごとに WOW-Engine の step メソッドを呼び出しパーティクルの座標を更新します。そのあと、パーティクルの座標を 3D オブジェクトに設定します。*2

サンプルソース

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import fr.seraf.wow.core.data.WVector;
    import fr.seraf.wow.core.WOWEngine;
    import fr.seraf.wow.primitive.WBoundArea;
    import fr.seraf.wow.primitive.WSphere;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.view.BasicView;
    
    [SWF(width="320", height="240", backgroundColor="#FFFFFF")]
    public class Sample003 extends Sprite
    {
        public function Sample003 ()
        {
            // 物理演算のために WOWEngine を作成
            var wow:WOWEngine = new WOWEngine(1);
            // 重力を設定
            wow.addMasslessForce(new WVector(0, -4, 0));
            
            // バウンドエリアを作成
            var boundArea:WBoundArea = new WBoundArea(300, 300, 300);
            // バウンドエリアを設定
            wow.setBoundArea(boundArea);
            
            // パーティクルを作成
            var particles:Array = [];
            for(var i:int=0; i<3; i++) {
                // (i*100, i*100, i*100) の位置に 半径 30 の固定されていない球を 重さ 1 反発係数 0.9 で作成
                particles[i] = new WSphere(i*100, i*100, i*100, 30, false, 1, 0.9);
                // WOWEngine に球を追加
                wow.addParticle(particles[i]);
            }
            
            // 表示のために PaperVision のビューを作成
            var view:BasicView = new BasicView();
            view.camera.y = 100;
            view.camera.x = 300;
            addChild(view);
            view.startRendering();

            // バウンドエリアに対応する 3D オブジェクトを作る
            var cube:Cube = new Cube(new MaterialsList({all:new WireframeMaterial(0x333333)}), 300, 300, 300,  3, 3, 3);
            view.scene.addChild(cube);
            
            // パーティクルに対応する 3D オブジェクトを作る
            var spheres:Array = [];
            for(i=0; i<3; i++) {
                spheres[i] = new Sphere(new WireframeMaterial(0x000099<<(i*8)), 30, 30, 30);
                view.scene.addChild(spheres[i]);
            }
            
            // 毎フレームごとの処理
            var n:int=0;
            addEventListener(Event.ENTER_FRAME, function():void {
                // 計算を行い WOW-Engine のオブジェクトを次の位置へ
                wow.step();
                // 新たな位置を PaperVision3D のオブジェクトへ設定
                for(var i:int=0; i<3; i++) {
                    spheres[i].x = particles[i].px;
                    spheres[i].y = particles[i].py;
                    spheres[i].z = particles[i].pz;
                }
                // バウンドエリアを回転
                boundArea.setRotation((int(n/180)%2==0)?n:0, 0, (int(n/180)%2==0)?0:n);
                // 合わせて表示オブジェクトも回転
                cube.rotationX = (int(n/180)%2==0)?n:0;
                cube.rotationZ = (int(n/180)%2==0)?0:n;
                n+=4;
            });
        }
    }
}

*1:パーティクル同士をつなぐバネや重力を設定したりすることでパーティクルの動きを変化させることはできますが、WOW-Engine から得られる物理演算結果はパーティクルの座標だけです

*2:この時、一つ注意しなければならない事があります。サンプルのソースで setRotation メソッドを使用してバウンドエリアを回転させていますが、Papervision3D と WOW-Engine で回転に関する仕様が異なるために、複数の軸に対して回転を設定すると表示上のオブジェクトとバウンドエリアの姿勢の整合性が取れなくなります。回転を設定するときは一つの軸に対してのみ設定するようにしてください。