2008.01 Monthly archives

[Papervision3D2.0] InteractiveScene3DEvent

2008.01.30

ISM(InteractiveSceneManager)が登場して、FaceLevelイベントがサポートされたあたりから訳解んなさ全開だったマウスイベント関連が、かなりスッキリして扱いやすくなった印象。試してみたのが以下のデモ。

InteractiveTest.swf(要:FlashPlayer9)
※各キューブがマウスオーバー、アウト、クリックに反応。

DisplayObject3Dインスタンスでマウスイベントを取得する場合は、

viewport = new Viewport3D(0,0,true,ture);

var flmat:FlatShadeMaterial = new FlatShadeMaterial(light, 0x00ccff );
flmat.interactive = true;

var objPlane:Plane = new Plane( flmat, 250, 250);
objPlane.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, mouseClick);

private function mouseClick(event:InteractiveScene3DEvent):void
{
    //イベント発生時の処理
    event.displayObject3D.pitch(90);
}

なんて感じで、Viewport定義の際にinteractiveプロパティをtrueにして、イベントリスナーを登録したいDisplayObject3Dインスタンスに与えるマテリアルのinteractiveプロパティをtrueに。この状態でDisplayObject3DインスタンスにaddEventListerすればいいと。

取得できるマウスイベントの種類は、

InteractiveScene3DEvent.OBJECT_CLICK
InteractiveScene3DEvent.OBJECT_OVER
InteractiveScene3DEvent.OBJECT_OUT
InteractiveScene3DEvent.OBJECT_MOVE
InteractiveScene3DEvent.OBJECT_PRESS
InteractiveScene3DEvent.OBJECT_RELEASE
InteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDE ※未実装

あと、マウスイベントじゃないけど

InteractiveScene3DEvent.OBJECT_ADDED

ってのもある。

今回の仕様変更で、入れ子にしたDisplayObject3Dインスタンスでもきちんとイベント取れるようになった。
1.7の頃のようにGroup.asとか使わなくていいのはうれしげ。

08/02/20追記
l4lさんによるとInteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDEは、イベント名の定義だけで実際の処理は未実装だそうな。ちゃんと確認しないとダメね>オレ

さらに追記
OBJECT_RELEASE_OUTSIDEについてもう少し調べてみたところ、AS3のマウスイベントは以下のような仕様になっているらしい。

マウスイベントはそのインスタンス上でしか発生しないため releaseOutside を実現するためには stage にリスナー登録する必要が出てきます。
void element blog: ボタン作成のためのイベントフロー制御

実際どうすりゃいいのか良くわかんなかったんで、上記のvoid element blogさんや、「Adobeデベロッパーセンター:ActionScript 3.0のイベント処理について」を参考にして、一応ソレっぽく動くものを作ってみた。

releaseOutsideテスト

OBJECT_PRESSして、カーソルを動かさずそのままマウスボタンを離した場合の「mouseRelease」と、OBJECT_PRESSしたまま、マウスカーソルを何もオブジェクトが無い場所や、他のCubeの上で離した場合の「mouseReleaseOutside」をmousePressハンドラ内で定義したら何となく問題なさそうな動きになった。ソース見たほうが早いと思うので貼っておく。このやり方じゃダメダメな気がしてならねぇ。超自信無い。

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package
{
    import flash.display.*;
    import flash.events.*;
 
    import caurina.transitions.Tweener;
 
    import org.papervision3d.events.InteractiveScene3DEvent;
    import org.papervision3d.view.BasicView;
    import org.papervision3d.core.utils.InteractiveSceneManager;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.materials.special.CompositeMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
 
    [SWF(backgroundColor=0x000000)]
 
    public class InteractiveTest2 extends BasicView
    {
        private var targetObj:DisplayObject3D;
 
        public function InteractiveTest2()
        {
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            //viewportの定義とカメラタイプ定義
            super (0,0,true,true,"Target");
 
            //カメラ設定
            camera.x = 600;
            camera.y = 600;
            camera.z = -2000;
            camera.fov = 30;
 
            //マテリアル設定
            var colorMat:ColorMaterial = new ColorMaterial( 0x006699, 1 );
            var wireMat:WireframeMaterial = new WireframeMaterial( 0x0099cc );
            var compoMat:CompositeMaterial = new CompositeMaterial();
            compoMat.addMaterial(colorMat);
            compoMat.addMaterial(wireMat);
            compoMat.interactive = true;
 
            var fmaterials:MaterialsList = new MaterialsList(
            {
                all:    compoMat
            });
 
            //Cube生成
            var tx:Number = -270;
            var ty:Number = 0;
            var tz:Number = 0;
 
            var objCube1:Cube = new Cube( fmaterials, 250, 250, 250, 1, 1, 1);
            scene.addChild(objCube1, "cube1");
            objCube1.x = tx;
            objCube1.y = ty;
            objCube1.z = tz;
            objCube1.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, mousePress);
 
            tx = 270;
            var objCube2:Cube = new Cube( fmaterials, 250, 250, 250, 1, 1, 1);
            objCube1.addChild(objCube2, "cube2");
            objCube2.x = tx;
            objCube2.y = ty;
            objCube2.z = tz;
            objCube2.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, mousePress);
 
            var objCube3:Cube = new Cube( fmaterials, 250, 250, 250, 1, 1, 1);
            objCube2.addChild(objCube3, "cube3");
            objCube3.x = tx;
            objCube3.y = ty;
            objCube3.z = tz;
            objCube3.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, mousePress);
 
            //レンダリング開始
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            super.onRenderTick(event);
        }
 
        //OBJECT_PRESS イベントリスナー
        private function mousePress(event:InteractiveScene3DEvent):void
        {
            Tweener.addTween(
                event.displayObject3D,
                { scaleZ:0.5, time:0.8, transition:"easeoutelastic" }
            );
            targetObj = event.displayObject3D;
            targetObj.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, mouseRelease);
            stage.addEventListener( MouseEvent.MOUSE_UP, mouseReleaseOutside );
        }
 
        //OBJECT_RELEASE イベントリスナー
        private function mouseRelease(event:InteractiveScene3DEvent):void
        {
            Tweener.addTween(
                event.displayObject3D,
                { scaleZ:1, time:0.8, transition:"easeoutelastic" }
            );
            event.displayObject3D.removeEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, mouseRelease);
        }
 
        //stage.MouseEvent.MOUSE_UP イベントリスナー
        private function mouseReleaseOutside(event:MouseEvent):void
        {
            Tweener.addTween(
                targetObj,
                { scaleZ:2, time:0.5, transition:"easeoutelastic" }
            );
            stage.removeEventListener( MouseEvent.MOUSE_UP, mouseReleaseOutside );
        }
 
    }
}

※コンパイルにはTweenerも必要

テストついでに、階層化されたオブジェクトのマウスイベントについても改めて動作確認してみた。3つのCubeは左から「親」「子」「孫」の階層になっていて、それぞれ問題なくマウスイベントが取れているみたいだ。

[Papervision3D2.0] meshSort

2008.01.29

DisplayObject3Dのプロパティ「meshSort」を、TriangleMesh3Dがちゃんと処理するようにしてみた。
TriangleMesh3D.asの135行目あたりの

if((iFace.visible = triCuller.testFace(face, vertex0, vertex1, vertex2))){
    screenZs += iFace.screenZ = (vertex0.z + vertex1.z + vertex2.z)/3;
    rc = face.renderCommand;
    visibleFaces++;
    rc.renderer = mat as ITriangleDrawer;
    rc.screenDepth = iFace.screenZ;
    renderSessionData.renderer.addToRenderList(rc);
}else{
    renderSessionData.renderStatistics.culledTriangles++;
}

の部分を、

if((iFace.visible = triCuller.testFace(face, vertex0, vertex1, vertex2))){
    switch(this.meshSort)
    {
        case DisplayObject3D.MESH_SORT_CENTER:
            screenZs += iFace.screenZ = ( vertex0.z + vertex1.z + vertex2.z )/3;
            break;

        case DisplayObject3D.MESH_SORT_FAR:
            screenZs += iFace.screenZ = Math.max(vertex0.z,vertex1.z,vertex2.z);
            break;

        case DisplayObject3D.MESH_SORT_CLOSE:
            screenZs += iFace.screenZ = Math.min(vertex0.z,vertex1.z,vertex2.z);
            break;
    }
    rc = face.renderCommand;
    visibleFaces++;
    rc.renderer = mat as ITriangleDrawer;
    rc.screenDepth = iFace.screenZ;
    renderSessionData.renderer.addToRenderList(rc);
}else{
    renderSessionData.renderStatistics.culledTriangles++;
}

って感じでmeshSortの値に応じたfaceの深度を指定するようにswitch文を足す。PV3D1.7のMesh3Dそのまんま。
renderがポリゴンを描画する際に、RenderListに登録されたRenderTriangleのscreenDepthと可視判定の結果を元にするので、ここでの深度決定が重要みたいだ。
[Papervision3D2.0] BitmapViewportMaterialのガテン系ボーカロイドはこれでポリゴン欠けを低減したんだけど、なぜかDAE.asだとmeshSortが無視されるためCollada.asで。

meshSortプロパティ使ってもZ-Sortの問題が解決されるわけじゃないけど無いよりマシかと。PV3Dの開発陣がこれを実装してなかったのは意図的な気もするので、何か別の方法を模索していることを期待したい。

[Papervision3D2.0] EnvMapShader

2008.01.28

いっぱいあるシェーダーから、EnvMapShaderを試してみた。

e_envmap.jpg

shaderTest.swf(要:FlashPlayer9)※すんげー重いです

重いー。相当制限付けて使わないと実用性なさげ。
EnvMapShaderも然る事ながら、床の写り込みはかなり無茶だった。pv3d.orgの手法をまんま使ったんだけど、これって完全なフェイクなので一発ネタ専用な感じ。

あと、Collada.asでDAE読む時に

mList.addMaterial(matEnv, "hoge");
obj_dae = new Collada("fuga.dae", mList);

とかいう感じでShader系のマテリアルを投げるとおかしな事になった。Shader系以外のColorMaterialとかBitmapMaterialなら大丈夫なのに。

DAE.as使って

mList.addMaterial(matEnv, "hoge");
obj_dae = new DAE(false);
obj_dae.load( "fuga.dae", mList );

だとShader系マテリアルでもちゃんと反映される。

[Papervision3D2.0] BitmapViewportMaterial

2008.01.24

次はマテリアルをサラっと。
Interactive〜とか、Precise〜とかの機能別派生クラスは全部無くなって、プロパティ等で設定するように変更されて随分スッキリした。目玉はPoitLight3Dを絡めたshader系のマテリアルなんだろうけど、バグが多いのが困りもの。追加されたもの以外は1.7から変わって無いと思う。あまりキチンと検証してないけど。

shader系については、formerさんのこのエントリと、このエントリが素晴らしく有益。

マテリアルを端から全部検証する気力が今のとこ無いので、地味に面白い新マテリアル「BitmapViewportMaterial」を試してみた。

e_viewportmaterial.jpg

viewportMaterialTest.swf(要:FlashPlayer9)※重いです

BitmapViewport3Dっていう、Viewport3D派生の「ビューポートの状態をビットマップ化」するクラスが生成したビットマップをそのままマテリアルにしちまおうというのが、BitmapViewportMaterial。

var bitmapView:BitmapViewport3D = new BitmapViewport3D();
var viewMat:BitmapViewportMaterial = new BitmapViewportMaterial(bitmapView);
var obj_Plane:Plane = new Plane(viewMat, 4096, 3072, 4, 4 );
addChild(obj_Plane);

とかいう定義をしておいて、
ループ内で

renderer.renderScene(scene,camera,bitmapView);

で、レンダリングすればマテリアルに反映されると。

上のデモは2つのカメラを配置して、顔のアップ用カメラの視界をBitmapViewport3Dに出力した。「3D空間内を撮影」なんていう機能の実装に使えそう。

[Papervision3D2.0] objects.special

2008.01.13

objectsパッケージのラストを飾るのは、special。
このパッケージは、何とも使いどころが難しそうなクラスが並んでおりますな。

  • Frustum3D.as ※リビジョン639で削除されました
  • ParticleField.as
  • Sound3D.as
  • UCS.as

ParticleFieldは、Starsクラスに替わってPV3D1.7の後期リビジョンで追加されたものと同じ。
それ以外のクラスは2.0で新たに登場したもの。

Frustum3Dのコンストラクタ定義

Frustum3D ( FrustumCamera3Dオブジェクト:FrustumCamera3D )

これは、FrustumCamera3Dの視錐台を可視化するクラス。3Dアプリケーションなんかでよく見かけるものだけど、デバッグとか、カメラのモーションエディタとか作るのに使う以外、用途がひらめかねぇ。
08/09/01追記:
Rev.639以降は、Frustum3Dクラスが削除されたので使えない。一応記述は残しておくけど御注意を。

使用例

var frustum_camera:FrustumCamera3D = new FrustumCamera3D(viewport, 28, 200, 2000 );
frustum_camera.y = 100;
frustum_camera.z = -400;

var objFrustum:Frustum3D = new Frustum3D(frustum_camera)
scene.addChild( objFrustum );
objFrustum.y = frustum_camera.y;
objFrustum.z = frustum_camera.z;

カメラとFrustum3Dオブジェクトは別々のDisplayObject3Dオブジェクトなので、カメラを移動してもFrustum3Dはついてこない。同期させるには適時カメラ座標をFrustum3Dに渡すなり、ペアレントするなり。

ParticleFieldのコンストラクタ定義

ParticleField (
パーティクルマテリアル:ParticleMaterial,
パーティクルの量:int,
パーティクルを分布させる空間の幅:Number,
パーティクルを分布させる空間の高さ:Number = 2000,
パーティクルを分布させる空間の奥行:Number = 2000
)

以前との違いは、マテリアルの指定が直接RGB値だったのが、パーティクル専用マテリアルのParticleMaterialに変わったくらい。ParticleMaterialは、org.papervision3d.materials.special に。

使用例

var pMat:ParticleMaterial = new ParticleMaterial( 0xffffff,1 );
var PF:ParticleField = new ParticleField( pMat, 1000, 1000, 1000,1000 );
scene.addChild(PF);

Sound3Dのコンストラクタ定義

Sound3D ( サウンドオブジェクト:Sound )

カメラとの位置関係によって、割り当てたSoundオプジェクトのボリュームやパンを変化させて、なんちゃって3Dサウンドが楽しめるクラス。サウンドビジュアライザー的なものに使えば付加価値上がってよろしいんじゃないかと。

使用例

var soundurl:URLRequest = new URLRequest("hoge.mp3");
var sound_obj:Sound = new Sound( soundurl );

var objSound3D:Sound3D = new Sound3D( sound_obj );
scene.addChild(objSound3D);
objSound3D.play();

Sound3Dオブジェクトは、DisplayObject3Dを継承したクラスなので、

objSound3D.x = 1000;
objSound3D.y = 0;
objSound3D.z = 1000;

とかいう感じで座標指定してやると、音の定位が変化する。また、バグか仕様か、Sound3Dオブジェクトとcameraの座標が重なると音が出ないので注意。

UCSのコンストラクタ定義

UCS (
拡大縮小率:Number,
インスタンス名:String
)

これは文字コード関係のUCSではなく、座標軸を作成するクラス。

使用例

var objUCS:UCS = new UCS( 200 );
scene.addChild(objUCS);

Frustum3Dと同様、デバッグとか空間認識のためのインフォメーションに使うのかしら。Frustum3Dと、UCSに関しては、その親クラスであるLines3Dの使用例って感じだな。

以上でobjectsパッケージは一通り目を通した。(カチンシュボパチン)ふー。
今回のサンプルは3つ。

sp_UCS_Frustum3D.swf(要:FlashPlayer9)
sp_ParticleField.swf(要:FlashPlayer9)
sp_Sound3D.swf(要:FlashPlayer9)※音出るのでご注意。

地味だな…orz。