note.x

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は左から「親」「子」「孫」の階層になっていて、それぞれ問題なくマウスイベントが取れているみたいだ。


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の開発陣がこれを実装してなかったのは意図的な気もするので、何か別の方法を模索していることを期待したい。


いっぱいあるシェーダーから、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系マテリアルでもちゃんと反映される。


次はマテリアルをサラっと。
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空間内を撮影」なんていう機能の実装に使えそう。


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。


objects パッケージの続き。今度は parsers。このパッケージには、3Dアプリケーション等で作った形状データを読み込んでPV3D内で扱うクラス郡がまとまってる。

  • ascolladaフォルダ:ascolladaで使うクラス郡
    • Node3D.as
    • Skin3D.as
  • Ase.as:ASEファイル(.ase)を扱うクラス
  • Collada.as:COLLADAファイル(.dae)を扱うクラス
  • DAE.as:COLLADAファイル(.dae)を扱うクラスのascollada版
  • MD2.as:Quake 2 MD2ファイル(.md2)を扱うクラス

ascolladaは、なんだかPV3D専用っぽくなってきちゃったけど、ActionScriptで汎用的にColladaを取り扱うためのクラスライブラリとして、Tim Knip氏が作ったもの。SandyやAway3Dでも使えるはずだけど、DAE.as に相当するクラスが提供されてないっぽいので、PV3D以外で使いたい場合は、ここを自力でなんとかしないといけないのかな。

parsers パッケージは、ascolladaが標準で同梱されたのと、MD2.as が加わった以外はPV3D1.7までと変わってない感じ。

Aseのコンストラクタ定義

Ase (
マテリアル:MaterialObject3D,
ファイル名:String,
拡大縮小率:Number=1,
各種プロパティ初期値:Object
)

ASE(ASCII Scene Export)って、知ってる限りだと3dsMAXくらいしか書き出しできる環境知らないので、使ったことないし、多分使わないので動作確認してない。

Colladaのコンストラクタ定義

Collada(
COLLADAのXMLデータ :*,
マテリアル:MaterialsList,
拡大縮小率:Number,
各種プロパティ初期値:Object
)

使用例

var objCollada:Collada = new Collada( "hoge.dae" );

DAEのコンストラクタ定義

DAE( async:Boolean )

async と定義されたパラメータの意味がよくわかんない。XMLをパースして頂点定義するあたりで使ってるみたいで、asyncの真偽で頂点データの格納方法振り分けてた。直感的にアニメーションに関係あるのかなぁとか思った。この辺は追加調査。

DAEは他と違って、メッシュデータを実際に読み込むには.load()メソッドを使う。

使用例

var obj_dae:DAE = new DAE();
obj_dae.load("hoge.dae");

ascollada経由のDAEと、PV3D標準のColladaの2つのクラスがあるけど、DAEだけ使ってりゃ良いような気がしなくもない。DAEならSketchUpが吐いたファイル読めるし、.daeファイルさえ正しく書き出せるならアニメーションにも対応してるし。

MD2のコンストラクタ定義

MD2 (
マテリアル:MaterialObject3D,
ファイル名:String,
アニメーションの再生フレームレート:int,
拡大縮小率:Number = 1,
各種プロパティ初期値:Object
)

使用例

var bitmapMat:BitmapFileMaterial = new BitmapFileMaterial( "fuga.jpg" );
var obj_md2:MD2 = new MD2( bitmapMat,"hoge.md2" );

D3S Importer LibraryのMD2部分のみをPV3D2.0で使えるようにした感じ。アニメーションさせるには、Scene3Dをする際に、

scene = new Scene3D(true);

として、アニメーションを有効にする必要あり。

parsersはだいたいこんな感じか。ascolladaとか詳細に調べてるとキリがねーので、必要になるたびに調べていこうっと。parsersのデモとして、ガテン系ボーカロイドをDAE経由で、以前も使ったQuake2のデータをMD2でそれぞれ読み込んでみた。

e_parsers01.jpge_parsers02.jpg

Parsers_dae.swf(要:FlashPlayer9)
Parsers_md2.swf(要:FlashPlayer9)

かぐぁみねメッシュデータの作者は、ねぎミクと同様ズサさん。.mqoファイルをコンバートしてポリゴン削減後にColladaで出力したもの。これ作って気付いたんだけど、なんかPV3D2.0って meshSort が未実装っぽい。DisplayObject3DにはmeshSortプロパティあるんだけど、肝心のTriangleMesh3D.as側で何も処理してない気がする。せっかくパーツばらしたのにー。

太郎日記’79Jさんでも、ロードローラー娘が回っております。仕事早すぎ。


PV3D2.0では、メッシュ形状を扱うクラス郡 objects パッケージの構成がかなり変わった。
用途別に、

  • papervision3d.objects.parsers
  • papervision3d.objects.primitives
  • papervision3d.objects.special

と、3つに分類されたみたいだ。
とりあえず primitives から調べてみる。

primitivesパッケージには

  • Cone.as :円錐
  • Cube.as :立方体
  • Cylinder.as :円柱
  • PaperPlane.as :紙飛行機
  • Plane.as :平面
  • Sphere.as :球体

の6つのプリミティブ形状定義クラスが含まれる。OldCubeが無くなっちゃった。
それぞれの使い方はこれまでと一緒。

Coneのコンストラクタ定義

Cone (
マテリアル:MaterialObject3D,
底面の半径:Number,
高さ:Number,
幅方向の分割数:int,
高さ方向の分割数:int,
各種プロパティ初期値:Object
)

使用例

var objCone:Cone = new Cone( material, 150, 300, 8, 6 );

Cubeのコンストラクタ定義

Cube (
マテリアルリスト:MaterialsList,
幅:Number,
奥行:Number,
高さ:Number,
幅方向の分割数:int,
奥行方向の分割数:int,
高さ方向の分割数:int,
内側に向ける(法線を逆転させる)面の指定:int,
描画しない面の指定:int,
各種プロパティ初期値:Object
)

※内側に向ける面の指定と描画しない面の指定には、Cube.NONE(指定しない)、Cube.ALL(全て)、Cube.FRONT、Cube.BACK、Cube.RIGHT、Cube.LEFT、Cube.TOP、Cube.BOTTOM を使う。また、Cube.FRONT + Cube.BACK、Cube.ALL – Cube.RIGHT とか記述することで複数指定も可能。

使用例

var objCube:Cube = new Cube( materials, 250, 250, 250, 1, 1, 1 );

Cylinderのコンストラクタ定義

Cylinder (
マテリアル:MaterialObject3D,
底面の半径:Number,
高さ:Number,
幅方向の分割数:int,
高さ方向の分割数:int,
上面の半径:Number,
各種プロパティ初期値:Object
)

使用例

var objCylinder:Cylinder = new Cylinder( material, 150, 300, 8, 6 );

PaperPlaneのコンストラクタ定義

PaperPlane (
マテリアル:MaterialObject3D,
拡大倍率:Number,
各種プロパティ初期値:Object
)

使用例

var objPaperPlane:PaperPlane = new PaperPlane( material, 2 );

Planeのコンストラクタ定義

Plane (
マテリアル:MaterialObject3D,
幅:Number,
高さ:Number,
幅方向の分割数:int,
高さ方向の分割数:int,
各種プロパティ初期値:Object
)

使用例

var objPlane:Plane = new Plane( material, 300, 300, 2, 2 );

Sphereのコンストラクタ定義

Sphere (
マテリアル:MaterialObject3D,
球の半径:Number,
幅方向の分割数:int,
高さ方向の分割数:int,
各種プロパティ初期値:Object
)

使用例

var objSphere:Sphere = new Sphere( fmaterial, 150, 12, 8 );

デモとして、プリミティブ形状を並べてみた。

e_primitives.jpg

PrimitiveObjects.swf(要:FlashPlayer9)

デモには以前に取り上げた TorusHemisphereGeodesicSphere をPV3D2.0用に修正して加えてある。Mesh3DからTriangleMesh3Dへ変更になったことに関連する部分のみの修正だけで済んだ。

あと、オレが使ってるリビジョンだとPaperPlaneの定義でエラーが出た。原因は例によってTriangle3Dの定義の際にUV座標が指定されてないからだったんだけど、これって仕様じゃなくてバグなのかなぁ。とりあえず超テキトーにNumberUV定義して突っ込んだら動いたけど、テクスチャ貼ったらメチャメチャだと思う。


PV3D1.7までは、自前メッシュ定義のためにMesh3Dクラスが用意されてたけど、PV3D2.0でこれがTriangleMesh3Dクラスに変更された。名前が変わっただけっぽいけどメッシュ定義の手続きが若干違うみたいだったので実際やってみた。

TriangleMesh3Dのコンストラクタ定義

TriangleMesh3D(
    material:MaterialObject3D,
    vertices:Array,
    faces:Array,
    name:String=null,
    initObject:Object=null
)
  • material:メッシュに与えるマテリアル
  • vertices:頂点座標(Vertex3Dオブジェクト)の格納された配列
  • faces:面定義(Triangle3Dオブジェクト)の格納された配列
  • name:メッシュのnameプロパティ
  • initObject:各種プロパティの定義Object

自前メッシュを作るには、

  1. マテリアルを定義
  2. 定義したマテリアル、空の頂点Array、空の面ArrayでTriangleMesh3Dオブジェクトを生成
  3. 頂点座標を定義してgeometry.verticesに追加
  4. 頂点座標を元に面を定義してgeometry.facesに追加

て感じで、PV3D1.7とおんなじ流れでいいみたいだ。

んで出来上がったのが以下。三角ポリ一枚定義しただけ。

e_trianglemesh3d.jpg

TriangleMesh3DTest.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
package
{
    import flash.display.*;
    import flash.events.*;
 
    import org.papervision3d.view.BasicView;
    import org.papervision3d.core.geom.TriangleMesh3D;
    import org.papervision3d.core.geom.renderables.Triangle3D;
    import org.papervision3d.core.geom.renderables.Vertex3D;
    import org.papervision3d.core.math.NumberUV;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.special.CompositeMaterial;
 
    [SWF(backgroundColor=0x000000)]
 
    public class TriangleMesh3DTest extends BasicView
    {
        private var mesh:TriangleMesh3D;
 
        public function TriangleMesh3DTest()
        {
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            //viewportの定義とカメラタイプ定義
            super (0,0,true,false,"Target");
            init3D();
        }
 
        public function init3D():void
        {
            //カメラ設定
            camera.z = -1000;
            camera.fov = 30;
 
 
            //マテリアル設定
            var colorMat:ColorMaterial = new ColorMaterial( 0x006699, 1 );
            colorMat.doubleSided = true;
 
            //TriangleMesh3Dオブジェクト生成
            mesh = new TriangleMesh3D( colorMat, new Array(), new Array(), null );
            scene.addChild(mesh);
 
            //頂点定義
            var v0: Vertex3D = new Vertex3D( -80, -80, 0 );
            var v1: Vertex3D = new Vertex3D( 80, -80, 0 );
            var v2: Vertex3D = new Vertex3D( 0, 80, 0 );
            mesh.geometry.vertices.push( v0 );
            mesh.geometry.vertices.push( v1 );
            mesh.geometry.vertices.push( v2 );
 
            //面定義
            var uvA:NumberUV = new NumberUV( 0, 0 );
            var uvC:NumberUV = new NumberUV( 1, 0 );
            var uvB:NumberUV = new NumberUV( 0, 1 );
            mesh.geometry.faces.push( new Triangle3D( mesh, new Array(v0,v1,v2), null, new Array(uvA,uvC,uvB) ));
 
            mesh.geometry.ready = true;
 
            //レンダリング開始
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            mesh.rotationY += 2;
            super.onRenderTick(event);
        }
 
    }
}

ハマったのは面の定義。Face3Dクラスが無くなって、三角ポリの面定義にTriangle3Dクラスを使うことになった模様。

Triangle3Dのコンストラクタ定義

Triangle3D(
    do3dInstance:DisplayObject3D,
    vertices:Array,
    material:MaterialObject3D=null,
    uv:Array=null
)
  • do3dInstance:面が属するDisplayObject3D?(若干自信無い)
  • vertices:面を構成する頂点Array
  • material:面のマテリアル?(未調査)
  • uv:面のUV座標Array

ってとこまではすぐに理解できたんだけども、UVの定義が必須になったってのがわかんなくて、かなりてこずった。コンストラクタのデフォルト値がnullだったんで、UV定義無くてもColorMaterialとか使う分には問題無いかと思ってたらエラーで怒られた。

それにしても、Face3Dを廃止してFace定義を細分化したっぽいあたり、今後は四角ポリが使えるようになったりするのかしら。