note.x

particlesの件

Rev.228でparticlesオブジェクトが追加された。AbstractParticleを継承していろんなパーティクルが作れそう。今後はStarParticleを使った「ParticleField」が「Stars」の代替オブジェクトになるみたいなので試してみつつ、Starsとの速度比較をしてみた。ビルドに用いたのはいずれもPV3D Beta 1.7(Rev.231)。表示されるパーティクルの数は揃えたつもり。

obj_Stars_fps.swf … Starsを使ったもの
obj_PF_fps.swf … ParticleFieldを使ったもの
(要:FlashPlayer9)

お〜速いじゃん。ParticleFieldはともかく、パーティクルが使えるようになったのは面白いかも。Away3Dにはパーティクル実装してなかった気がするし、独自性が出て良さげ。
ParticleFieldの使い方はStarsとほぼ同じ。色の指定がColorMaterialから直接RGB値になったくらい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package {
 
    import flash.display.*;
    import flash.events.*;
 
    import org.papervision3d.scenes.*;
    import org.papervision3d.cameras.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.objects.particles.*;
 
    [SWF(backgroundColor = 0x000000)]
 
    public class obj_PF extends Sprite {
 
        // プロパティ
        private var container   : Sprite;
        private var scene       : MovieScene3D;
        private var camera      : FreeCamera3D;
        private var objPF       : ParticleField;
        private var valx        : Number = 0;
        private var valy        : Number = 0;
 
        // コンストラクタ
        public function obj_PF():void
        {
            stage.frameRate = 60;
            stage.quality   = StageQuality.MEDIUM;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align     = StageAlign.TOP_LEFT;
            addEventListener(Event.ENTER_FRAME, loop3D);
            stage.addEventListener(Event.RESIZE, onStageResize);
 
            init3D();
        }
 
        // 初期化
        private function init3D():void
        {
            // コンテナ生成
            container = new Sprite();
            addChild(container);
            container.x = stage.stageWidth  / 2;
            container.y = stage.stageHeight / 2;
 
            // シーン生成
            scene = new MovieScene3D( container );
 
            // カメラ生成
            camera = new FreeCamera3D();
            camera.z = 0;
            camera.focus = 500;
            camera.zoom = 1;
 
            // ParticleField生成
            var starSprite:Sprite = new Sprite();
            objPF = new ParticleField(900, 0xffffff, starSprite, 1000, 1000, 1000 );
            scene.addChild( objPF );
        }
 
        // EnterFrame
        private function loop3D( event:Event ):void
        {
            valx += container.mouseX / 50;
            valy += container.mouseY / 50;
            objPF.rotationY = valx;
            objPF.rotationX = valy;
            scene.renderCamera( camera );
        }
 
        private function onStageResize(event:Event):void
        {
            container.x = stage.stageWidth  / 2;
            container.y = stage.stageHeight / 2;
        }
 
    }
}

07/11/13追記
カメラが原点に配置してあるとFlashPlayerのリビジョンによって問題が発生することが判明したため、上記のswfとソースを修正しました。詳細については「[Papervision3D] 原点にカメラがあるとフリーズする件」にて。


「こ、これだって、PV3DとAPEの連携なんだからねっ!」

e_apepv3d_test.jpg

ApePV3D_Test.swf(要:FlashPlayer9)

AbstractItemのspriteをsceneにしてSphereをaddChildしただけ。っていうか日本語喋れっていうか。発端はVOO-DOO-DOLLSにPV3Dのオブジェクトぶら下げようとしたんだけど、出来なくって自力でやってみた。苦労したわりにあんまり面白いものにはならなかった(泣)かわいいキャラクタぶら下げたほうがよっぽど和む。

頂点をAPEで動かすとなると大変そうだ。wow-engineって開発中止になっちゃったのかなぁ?


来るべきPhysics時代(何)に備えてAPE(Actionscript Physics Engine)を触ってみる。とりあえず、パーティクル掴んでポイポイできるようにしてみた。もっと簡単にできると思ってたら結構めんどうだった。付属のデモにはマウスイベント絡めたものが無くて、GoogleGroup – APE Generalの「Tunnelling Problem w/ Interactivity」が参考になった。

e_apestudy.jpg

Ape_study.swf(要:FlashPlayer9)

なんか挙動がおかしいのを何とかしたいのと、もうちょっと軽くしたいところ。


PV3Dの座標系について

PV3Dのチートシートにワールド座標系の解説図が載ってて、これによると画面手前側が「+Z」奥側が「-Z」という、いわゆる「右手座標系」ってことになってる。確か公式にも「right-handed coordinate system」みたいな記述がされていたのを見た記憶がある。

これまで作った自分のデモのソースを見ると「camera.z = -500」ってな記述がほとんど。これは、PV3Dのサンプルコードの中でcameraオブジェクトの座標指定を「camera.z = -500」とかやってるものが多かったんで、何の疑いもなく「カメラの位置を画面手前側に移動してる」と思って、それに倣ってきたんだけどPV3Dが右手座標系ならこの解釈はおかしいということになる。今更すぎるけどPV3Dの座標系がどうなってんのか調べてみた。

worldAxis.swf(要:FlashPlayer9)

cameraオブジェクトは「X:0,Y:0,Z:-800」の位置に置いた。この状態でobjCubeの動きと数値を見て、一般的な右手座標系の場合と比較してみると、Z軸の正方向とY軸の回転方向が一般的な右手座標系とは逆になってる。

※矢印の向きが正方向

これって何系よ?こういうのも右手座標系って呼ぶんだろうか。
Face3Dを定義する場合に半時計周りに頂点を登録しないと裏向きになるので、このあたりは右手座標系っぽいんだけど、左と右が混在してる感じでなんか混乱するぞ。調査方法がおかしいのかなぁ。


Update Version 1.7(Rev 200)

svnリポジトリにて、PV3D Version 1.7(Rev 200)が公開。

  • MovieAssetMaterialのバグフィックス
  • FaceLevelInteractivityブランチのマージ
  • docs(クラスドキュメント)の更新
  • ISMのドラッグ処理(VirtualMouse経由でのclick/drag、press/releaseメソッドが利用可能に)

が、主な更新内容みたい。

個人的には、この更新のすぐ直後にコミットされた「PreciseBitmapMaterial」のほうがインパクトがデカかった。これはパースペクティブコレクト付きBitmapMaterialで、Away3Dのアルゴリズムが採用されたもの。キター!と思ってすぐに試してみたけど上手く動かない。バグ?それともFlex2SDKじゃダメなのかなぁ?あと、FaceLevelInteractivityがマージされたってんで、昨日のデモをコンパイルし直したら挙動がおかしくなった。ちょっと様子見てみよう。

追記
PreciseBitmapMaterial、色々いじってるうちに動いた。

Precise_Bitmap.swf(要:FlashPlayer9)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package {
 
    import flash.display.*;
    import flash.events.*;
 
    import org.papervision3d.core.*;
    import org.papervision3d.scenes.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.cameras.*;
    import org.papervision3d.materials.*;
 
    [SWF(backgroundColor = 0x000000)]
 
    public class Precise_Bitmap extends Sprite {
 
        [Embed(source='e_check.jpg')] public var Image:Class;
 
        // プロパティ
        private var container   : Sprite;
        private var scene       : Scene3D;
        private var camera      : Camera3D;
        private var rootNode    : DisplayObject3D;
        private var valx        : Number = 0;
        private var valy        : Number = 0;
 
        // コンストラクタ
        public function Precise_Bitmap():void
        {
            stage.frameRate = 60;
            stage.quality   = "LOW";
            stage.scaleMode = "noScale";
            stage.align = StageAlign.TOP_LEFT;
            this.addEventListener(Event.ENTER_FRAME, loop3D);
            init3D();
        }
 
        // 初期化
        private function init3D():void
        {
            // コンテナ生成
            container = new Sprite();
            addChild(container);
            container.x = stage.stageWidth  / 2;
            container.y = stage.stageHeight / 2;
            stage.addEventListener(Event.RESIZE, onStageResize);
 
            // シーン生成
            scene = new Scene3D( container );
 
            // カメラ生成
            camera = new Camera3D(null,6,100,{x:0,y:0,z:-1500});
 
            // rootNode生成
            rootNode = scene.addChild( new DisplayObject3D( "rootNode" ) );
 
            // Material生成
            var texture:BitmapData = new Image().bitmapData;
            var pbMaterial:PreciseBitmapMaterial = new PreciseBitmapMaterial( texture );
            var nmMaterial:BitmapMaterial = new BitmapMaterial( texture );
 
            // Cube生成
            var obj_Cube1:OldCube = new OldCube(pbMaterial, 500, 500, 500, 1, 1, 1,{x:-400});
            rootNode.addChild( obj_Cube1 );
            var obj_Cube2:OldCube = new OldCube(nmMaterial, 500, 500, 500, 1, 1, 1,{x:400});
            rootNode.addChild( obj_Cube2 );
        }
 
        // EnterFrame
        private function loop3D( event:Event ):void
        {
            valx += container.mouseX / 50;
            valy += container.mouseY / 50;
            rootNode.rotationY = valx;
            rootNode.rotationX = valy;
            scene.renderCamera( camera );
        }
 
        private function onStageResize(event:Event):void
        {
            container.x = stage.stageWidth  / 2;
            container.y = stage.stageHeight / 2;
        }
 
    }
}

比較のために、2つのCubeのうち片方にはBitmapMaterialを適用した。これまで、テクスチャが歪まないようにするにはポリゴンの分割数を上げるしかなかったけど、これで解決できそう。ただ、デモ作ってて気になったのが、カメラとオブジェクトの距離、カメラのfocusとzoomの値の組み合わせできちんと動かない場合があるような感じだった。いまいち法則性が掴めなかったんだけど、このマテリアル使うときはそのへんに注意かも。


InteractiveSceneManagerとかMouse3Dとか

PV3Dのsvnリポジトリに、FaceLevelInteractivityっていうブランチが出来てて、やたらハイペースでコミットされまくってたので何事かと思ってたら、RockOnFlashで紹介されてた。InteractiveMaterialをふまえた上での次のステップを開発中みたいで、Faceレベルでのマウスイベントサポート(?)とか、3Dワールド座標系→2D座標系への変換とかがサポートされるらしい…んだけど良くわかんない。とりあえずRockOnFlashの件のエントリにサンプルが紹介されてて、svn経由でソースがダウンロードできるので、これを拝借して試してみようと思ったら、FlashCS3でないとパブリッシュできないサンプルだった。ガックシ。

CS3持ってないので、Flex2SDKのみで動くように「helloMouse3D」のmain.asを改造してみた。

Mouse3dTest.swf(要:FlashPlayer9)

デモとしてはハデで楽しいんだけど、これで何作るかが問題だよなとつくづく思う。何かひらめくまでは貪欲に技術を吸収していくべ。

07/08/23追記
コメント欄でご質問いただいたので、現時点での最新版であるPV3D Rev.208を使う前提で、簡単に説明を追記してみる。

まずは、このリビジョンのMovieMaterial.asにバグがあるのでこれを修正する。これをやらないと何をやっても動かないです。

MovieMaterial.as 106行目
animatedMaterials[ this ] = initObject.animated || false;

animated = initObject ? initObject.animated || false : false;

「helloMouse3D」の改造ポイントに関しては、ほとんどオリジナルと変えてない。変えたのは大きく2箇所で、オリジナルのソース中でInteractiveMovieMaterialの設定してる部分と、マウスイベントの部分。Main.as89行目で

var material:MovieMaterial = new InteractiveMovieMaterial( new canvas() );

となっている部分の「new canvas()」が、.flaファイル内で定義されているクラスっぽかったんで、

var sp:Sprite = new Sprite();
sp.graphics.beginFill(0x0099cc);
sp.graphics.drawRect(0, 0, 500, 200);
sp.graphics.endFill();

var material:InteractiveMovieMaterial = new InteractiveMovieMaterial( sp );
material.allowAutoResize = false;

ってな感じでSprite作ってInteractiveMovieMaterialに投げた。上のデモはbeginGradientFillでグラデ塗りしたけど、この辺はお好きなように。allowAutoResizeをfalseにするのはかなり重要。あと、オリジナルでは「material.smooth = true」に設定してあるけど、パフォーマンスがかなり悪くなったんで消した。

次にマウスイベント。handleMouseDown、handleMouseMove内で、

canvas.material["movie"]["surface"].graphics

となっている部分を、

canvas.material["movie"].graphics

に変更。

だいたいこんな感じです。もっとスマートな方法があると思うし、オレ自身それほどAS3やPV3Dに精通してるわけじゃないので、バカっぽい事やってる可能性大ですが参考になれば幸いです。


ShadedColorMaterialを試す

Papervision3D wikiで追加プリミティブを提供してくれたりしてた、 Tim Knip氏がPV3Dのコアチームに参加との事で、svnリポジトリにブランチが出来てた。

早速、FrustumCamera3D.asっていう新しいカメラクラスが公開されてたんだけど、それとは別にメーリングリストでシレっと紹介されたShadedColorMaterialが、小技として使えそうだったんで試してみた。

Shaded.swf(要:FlashPlayer9)

使い方はすこぶる簡単で、

var shadeMaterial:ShadedColorMaterial = new ShadedColorMaterial( 0x00ccff );

ってな具合にColorMaterialと同じように指定するだけ。現時点のPV3Dはライトを実装してないので、疑似シェーディングでしかないけど今まで簡易的にすらシェーディングが提供されてなかったPV3Dで、オブジェクトに対して陰影が簡単に付けられるのはウレシげ。そのうちAway3D並のシェーディングは実装されると思うので、それまでの繋ぎで使えそうかも。

2007.9.1追記
疑似的ではあるものの、ShadedColorMaterialにリライティングが実装されたっぽいんで、デモのswfをビルドし直した。んだけど、svnに上がってるShadedColorMaterial.asを使うとfillcolorが無視されるという状態になってるので、メーリングリストで流れたパッチを当てて使った。


e_mt_fuji.jpg

適当なハイトマップから地形を作るってのは前に挑戦したので、今度は別のアプローチで地形生成に挑んでみる。

mt_Fuji.swf(要:FlashPlayer9 結構重いです。)

NASAが提供してる、SRTM-DEM(スペースシャトルからの測量で作成されたデジタル標高データだそうな)ってのがあって、精度が90mメッシュという制限付きながら、無料で利用できるってことだったので、このDEMデータを拝借して富士山を表示させてみた。
※この辺の話はTerragen使いの方々が詳しいようで、特にSRTM-DEMに関しては「散歩ジャーナル」さんの解説が超絶参考になりました。

やった作業は以下のような感じ。

  • SRTMのFTPサーバから目的のDEMデータをダウンロード。
  • ダウンロードしたDEMデータを「3DEM(DEMデータを扱うには定番らしい)」使って、Terragenデータにコンバート。
  • Terragenから、LWO(LightWaveのモデルデータ形式)でエクスポート。
  • BlenderでLWOを読み込んで、ポリゴンを地味に削る。UV貼り。
  • Colladaで書き出し。

DEMから直接PV3Dに読み込めたらカッコイイんだけど、オレにはそんな技量は無いので超ガテン系ですいません。

しっかしやっぱり重い。今回のデータは、頂点数:1592、フェイス:2886なんだけど、PPCのPowerBookだと相当ギリギリな感じ。でも、リアルな3D地形がFlashで動くのはなんだかウレシイ。PV3Dのcullingがもう少し進化すればもっと広い地形を飛び回ったりできるようになりそうな気がいたしますな。


ポリゴンcullingの具合はどうなのか

PV3D1.5で追加された新要素「culling」。カリングってのは視界外とか遮蔽されて見えなくなるポリゴンを間引きする処理。とりあえずどうやって使うのかが全然分かんね〜と思ってたら、GoogleCodeのDownloadページにJohn Grden氏によるデモがあった。
そのソースによると、

import org.papervision3d.core.culling.RectangleTriangleCuller;
var rect : Rectangle = new Rectangle(640*.5,320*.5,640,320);
scene.triangleCuller = new RectangleTriangleCuller(rect);

みたいな感じで、視界範囲をRectangleで指定した上で作成したRectangleTriangleCullerオブジェクトをscene3DのtriangleCullerに割り当てると、視界範囲外のFaceを間引いてくれるというシロモノらしいということが何となく分かった(我ながら何となくすぎる)ので、実際使ってみた。

Culling.swf(要:FlashPlayer9)
※サンプルは分かりやすいように視界範囲の色を変えてるけど、本来、視界範囲は見えない。

うーん。なんかこういう処理って、上下左右の視界より奥行きに対する間引きのほうが重要な気がする。(実際、DepthTriangleCullerっていうクラスも用意されてるけど、現状は中身が空だった。)イマイチ使いどころが分からないけど、ひょっとして画面に表示されるポリゴン数が同じでもcullingしたほうがパフォーマンスが良くなるのか?と思ったんで、比べてみた。

non_Culling.swf/culling無し(要:FlashPlayer9)
Culling_full.swf/cullingあり(要:FlashPlayer9)

cullingしたほうが微妙にパフォーマンスが良さげ。けど、でっかい地形データ上をウロウロできるようにしたいような時の実用性を考えると、カメラからの距離に応じた間引きって必須だと個人的には思うので、現時点ではAway3Dのocclusion culling実装の方が使い勝手よさそうな気がした。とりあえずDepthTriangleCullerの実装が楽しみ。