note.x

Away3D 3.6リリース。

Away3D 3.6.0: Flash 10 upgrades and the Influxis Battlecell API

実質、3.5.3リリース時の仕様をそのまま引き継ぐ形でメジャーアップグレード。予告通りNumber3D廃止、めでたくネイティブ 3D APIに完全移行。FlashPlayer9向けの2.x系は、メンテナンスアップデートのみ行われていくことになる模様。個人的には面白味が減ってしまった。

今回のデモは、FMSのホスティングやってるInfluxisと組んでRTMFP使ったマルチプレイヤーのFPS。4人まで同時接続できるらしいんだけど、海外サーバーな所為か2人でもうゲームにならなかった。趣旨と関係ないけど、マウスドラッグでエイミングはやりにくいな。

放置しっぱなしの過去エントリをどーするかねーと思いつつ、とりあえずView3Dに関するエントリを修正。


Away3DのFlashPlayer10版が大きく仕様変更して、MatrixAway3Dクラスが廃止。ビルトインクラスのMatrix3Dに置き換わった。なので、Object3Dのtransformプロパティの各要素を直接書き換えたりするような実装してる場合はMatrixAway3Dに関わる部分を変更しないとビルドできない。MatrixAway3DとMatrix3Dの各要素は下記の対応づけ。

MatrixAway3D

MatrixAway3D.sxx MatrixAway3D.sxy MatrixAway3D.sxz MatrixAway3D.tx
MatrixAway3D.syx MatrixAway3D.syy MatrixAway3D.syz MatrixAway3D.ty
MatrixAway3D.szx MatrixAway3D.szy MatrixAway3D.szz MatrixAway3D.tz
MatrixAway3D.swx MatrixAway3D.swy MatrixAway3D.swz MatrixAway3D.tw

ビルトインMatrix3D

Matrix3D.rawData[0] Matrix3D.rawData[4] Matrix3D.rawData[8] Matrix3D.rawData[12]
Matrix3D.rawData[1] Matrix3D.rawData[5] Matrix3D.rawData[9] Matrix3D.rawData[13]
Matrix3D.rawData[2] Matrix3D.rawData[6] Matrix3D.rawData[10] Matrix3D.rawData[14]
Matrix3D.rawData[3] Matrix3D.rawData[7] Matrix3D.rawData[11] Matrix3D.rawData[15]

値を読み取るだけなら、

var tx:Number = Object3D.transform.tx;

という記述を、

var tx:Number = Object3D.transform.rawData[12];

に置き換えることで対処できる。

要素の値を変更する場合、MatrixAway3Dの要領で

Object3D.transform.rawData[12] = 100;

なんて感じで直接代入しても変更が反映されないので、

var vec: Vector.<Number> = Object3D.transform.rawData;
vec[12] = 100;
Object3D.transform.rawData = vec;

と、Matrix3Dの仕様に準ずる。

また、MatrixAway3Dで実装されていたメソッドの

  • quaternion2matrix
  • compare

と、ゲッター/セッターとして定義されていた

  • forward
  • up
  • right

は、Matrix3DUtilsという新クラスが引き継いでる。
forward, up, rightについては読み取り専用でそれぞれ

  • getForward
  • getUp
  • getRight

というメソッドに変更になった。

あと、ベクトルについても主要な部分はNumber3DからVector3Dへ移行してる。
まだ一部でNumber3Dが使われている部分があるけど、最終的にはなくなるみたいなのでNumber3Dは忘れてよさげ。Number3Dに実装されてたgetAngleメソッドは、新クラスVector3DUtilsが引き継いでる。

今回の仕様変更がどの程度影響してるのか分からんけど、じわじわと高速化してきてうれしげ。新機能いらないから洗練してほしい。

2010/09/16追記
とか言ってるうちに、FP10版3.5.4にアップデート。
ArrayをVectorに置き換えたりという地味な最適化を目的としたものみたいだ。


Away3Dに BSP/PVS を実装しようとしてるブランチを見つけてニヤリ。
コミッターはFP10版Away3DにPixelbenderによるシェーディング実装した David Lenaerts
今どきのリアルタイムレンダリングシーンじゃ特に意識しないだろうし、Flash用エンジンに絞っても Alternativa3D とか infinity3d にはとっくに実装されてるし、これといったアドバンテージがあるわけじゃないけど、ライセンス的に緩めなAway3Dに組み込まれるのは本当にありがたい。

今のところObjectContainer3Dを継承したクラスとしてBSPTreeが実装されてて、空間分割されたコンテナをSceneに追加する形で使う。この辺はハナっからScene3DにBSPが組み込まれてる(と思われる)Alternativa3Dのほうがスマートだと頭の悪いオレでも思うので、今後の仕様変更でScene3Dに内包されるかも。そうなれば Zソート・クリッピング・カリング・ポリゴン交差時の分割処理に至るまでパイプラインの至る所で恩恵にあずかれるはず。あと、内部で持ってる物理演算や衝突判定の最適化にも取り入れようとしているみたいなので、ちょっぴり期待しておく。

まだまだガンガン仕様変更があると思うけど、どんな感じになるのか試したくて、以前作った迷路を使ってポータルシステムがどの程度カリングを最適化してくれるのかを見てみた。比較用にBSP/PVSを使わない場合も改めて作成。

BSPTreeとポータルの生成にはかなり時間がかかる。DoomやQuakeなんかだとレベルエディタを使って事前に作ったものを外部ファイルとして保存しておいて、実行時は読み込みだけで済むようにしてあるように、事前処理のがよさそう。Away3Dもこの辺は考えてあるようで、エクスポータークラスが用意されてる(PreFabにレベルエディタ的な機能が追加されたりして?)。今回は使い方がよくわかんねーのと、必要なデータ全部エクスポートするのこれ?って感じだったので諦めた。

Maze normal
Maze using BSP/PVS (BSP/PVS使用)

■操作方法
カーソルキー [CursorKey] … 移動 (move)
[Z], [X] Key … ティルト (Tilt)

※要:FlashPlayer10

BSP/PVS版は、スタンドアロンプレーヤーならかなりヌルヌル動く感じ。マシンパワーのある環境だと2つの違いが分かりにくいかもしれないけど、オレの環境(MacBookPro 2.33GHz Core2Duo)だと全然違う。クリッピングモードをRectangle Clippingにすると視覚的にも違いが見て取れる。Project stats の T-ELEMENTS と R-ELEMENTS を見ると処理対象のFaceが大幅に減ってるのが分かる。ワクワクしてまいりました。

ハードウェアの進化に伴って失われつつある先人達の技術を追体験しつつ手軽に勉強できるのが、ActionScriptで3Dをやる唯一のメリットだと思うので、こういうトピックは超アガる。ちゃんと自分のものにするには相当ハードル高いけど、とりあえず周辺をウロウロする。

参考:
BSP Trees(視覚的でわかりやすい)
バイナリ空間分割(Binary space partitioning)- Wikipedia
BSP trees in 3D worlds
ゲームプログラミングのためのリアルタイム衝突判定(Amazon)


以前アナウンスがあったAway3Dの高速化に関する話が、Away3D Liteとして結実。
svnリポジトリのtrunk入りしてたのが、ようやく正式発表されましたよっと。
最大の功労者であるsleepydesign の katopzに敬礼!Greate work katopz!

Away3D Lite v1.0: fastest and smallest 3d engine in Flash.

動作が軽いのと共に機能もライト、とりあえず最低限の機能のみ提供されてる感じ。
高機能化の果てに超重量級エンジンになっちゃった本家Away3Dとは別に、パフォーマンス重視専用のライブラリを別展開するという戦略なのか、単に本家の再設計が面倒だったのかはさておき、選択肢が増えたのはありがたいことであります。
どの程度の高速化がなされているかは上記のエントリに掲載されているデモで確認できる。

手元のデータでもやってみた。

away3dlitetest

Dae Performance Test(要:FlashPlayer10)
画面クリックで増殖、減らせないので注意。

sortTypeが上手く動作せずZソートがおかしいけど、3000△ポリ超えてもそこそこ動いてる。

.mqoを扱うクラスも標準提供されているので、メタセコのデータでも試してみた。
Mqo Performance Test(要:FlashPlayer10)
※モデルデータは、もとがしさんが配布されているものをお借りしました。

今までが1000△ポリあたりで限界だった事を考えると、このくらいのローポリモデルが扱えるようになったのはスゴイな。ビルボードとかPlaneとかを大量に使いたいみたいなケースにはもってこいかも。

触ってみた感触として、

  • 本家とは完全に別物であることを再確認。
  • APIがPV3Dの1.5〜1.7あたりと似た印象。
  • 未完成な感じが否めない(謎挙動・sortTypeのバグとか)

とか思った。

Matrix3Dなどのコアクラスが自前のものではなく、全部ビルトインクラスに置き換わってたりして、FP10の3D機能を体系化した3Dエンジンのひな形として参考になりそうではある。これをベースに自分好みのエンジンに調教するのもアリなんじゃねーかと。


ワーカーホリック野郎な日々を送ってる間にAway3Dがバージョンアップ。
Away3d: 2.4 & 3.4 released!

とりあえず地味な機能から試してみる。

ExplodeTest(要:FlashPlayer9)

Explodeクラスは、指定したObject3Dインスタンス内の全Faceを位置関係をそのままに、バラバラのMeshやFaceに分解してくれるジオメトリモディファイア。
Away3DはもともとFace単位でvisibleプロパティを持ってるので、表示/非表示をFace単位で行うことができていたけど、Face単位で大きさ変えたりすることはできなかった。その名のとおり、簡易的な爆発表現なんかに使えそうだし、演出面で結構使えるかもしれない。

具体的には以下のようにして使う。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import away3d.geom.Explode;
 
- 中略 -
 
//適当なメッシュを用意
var wcMat:WireColorMaterial = new WireColorMaterial(0x0099CC, {wirecolor:0x00CCFF});
var sphere:Sphere = new Sphere({material:wcMat, radius:100, segmentsW:8, segmentsH:6});
 
//Explodeでメッシュを分解
var _explode:Explode = new Explode(true, true);
var _explodedObj:ObjectContainer3D = _explode.apply(sphere) as ObjectContainer3D;
 
//分解されたメッシュが束ねられたObjectContainer3Dをシーンに追加
scene.addChild(_explodedObj);

Explodeのコンストラクタに渡す引数は以下のようになってる模様。

  • 第1引数:分解したFaceを個別のMeshとして定義するかどうか
    trueなら、Faceの数分Meshが生成される。falseなら、共有していた頂点をFaceの数だけ用意してそれぞれ単独の面として扱えるようにする。

  • 第2引数:再定義後のMeshの中心点をFaceの重心にもってくるかどうか
    falseなら、元になったObject3Dの原点が中心点のままになる

コンストラクタの第1引数をtrueにして、個別のMeshを生成した場合は、

for each(var item:Mesh in _explodedObj.children)
{
    item.scaleX = 0.5;
}

などとして、それぞれのMeshを制御する。

理屈では、Colladaメッシュにも適用できる。

dae Explode(要:FlashPlayer9)

ただ、現状のExplode.asには、元にしたメッシュがFace単位でマテリアル指定されているような場合、マテリアルが引き継がれないという問題があって、テクスチャが無効になってしまう。そこで、Explode.asを以下のように書き換えることで暫定的に対処した。ついでに「bothsides、backの要素が引き継がれない」という問題にも対処。

Explode.as 61行目

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
for(i=0;i<loop;++i){
 
    face = obj.faces[i];
    mesh = new Mesh();
 
    va = new Vertex(face.v0.x, face.v0.y, face.v0.z);
    vb = new Vertex(face.v1.x, face.v1.y, face.v1.z);
    vc = new Vertex(face.v2.x, face.v2.y, face.v2.z);
    uva = new UV(face.uv0.u, face.uv0.v);
    uvb = new UV(face.uv1.u, face.uv1.v);
    uvc = new UV(face.uv2.u, face.uv2.v);
 
    mesh.addFace(new Face(va, vb, vc, obj.material as ITriangleMaterial, uva, uvb, uvc));
    _container.addChild(mesh);
}

これを、

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
for(i=0;i<loop;++i){
 
    face = obj.faces[i];
    mesh = new Mesh();
 
    va = new Vertex(face.v0.x, face.v0.y, face.v0.z);
    vb = new Vertex(face.v1.x, face.v1.y, face.v1.z);
    vc = new Vertex(face.v2.x, face.v2.y, face.v2.z);
    uva = new UV(face.uv0.u, face.uv0.v);
    uvb = new UV(face.uv1.u, face.uv1.v);
    uvc = new UV(face.uv2.u, face.uv2.v);
 
    var setMaterial:ITriangleMaterial;
    if (obj.material) setMaterial = obj.material as ITriangleMaterial;
    else setMaterial = face.material as ITriangleMaterial;
 
    var fc:Face = new Face(va, vb, vc, setMaterial, uva, uvb, uvc);
    mesh.addFace(fc);
    _container.addChild(mesh);
    mesh.bothsides = obj.bothsides;
    fc.back = obj.back as ITriangleMaterial;
}

とする。

あとは、バラバラになった後のMeshが、もともとのFaceの法線方向をforwardベクトルにしてくれるといいのになぁと思ったけど面倒くさそうだったのでとりあえず保留。
また、Explodeコンストラクタの第1引数をtrueにして、FaceごとのMeshを生成すると当然負荷が増大するので、パフォーマンス重視の場合はMeshを生成せずに、Faceレベルでコントロールする方法を確立しないとダメっぽい。


Drive Test 04(要:FlashPlayer9)

[←] [→] キー … ハンドル
[z] キー … アクセル
[x] キー … ブレーキ
[↓] … バック

これまではトラクションの掛かり方がなんだか微妙で、ホイルスピンしっぱなしみたいだったのがようやく解消された感じ。旋回時にロールするようになったり、ブレーキングの時微妙にピッチングが発生したりするので、そこそこ感じが出てきた。

未だによくわかんないのが、JigLibFlashのJCarクラスのメソッド「setupWheel」で指定する wheelRestingFrac と、wheelDampingFrac の役割と関係性。コードを見る限り、それぞれサスペンションのバネレートとショックの強さに関係してるっぽいんだけど、頭の中のイメージと、実際に数値を指定してみた時の挙動がかみ合わない。ロールしたりする時にもっと「粘り」のようなものが欲しいんだけど、なんだかカクカクした感じになってる。物理演算のステップ数にも関係してそうだし、パフォーマンスの問題と拮抗しそうなので悩ましい感じ。

あと、JigLibFlashのvehiclesでモデル化されてる足回りのサスペンション形式がよくわからん。4輪それぞれにストローク幅が設定できてたりするし、ジオメトリ変更(トーとかキャンバーとか)が実装されてなさそうなので、なんちゃってストラットっぽい感じだけどもどうなんだろ。

正確なシミュレーションがしたいというよりは「感じを出したい」だけだし、このままだと「俺と足回り」とかいうテーマで一生を終えてしまいそうなので、このへんを深追いするのは一回やめにして駆動系に手を付けよう。JigLibFlashでモデル化されてる駆動系はCVTっていうか「駆動輪にモーター直結」なので、クラッチとミッションを追加することを次の目標にしてみるべ。


BezierPatch Test(要:FlashPlayer9)

いわゆるサブサーフェス(Subdivision Surface)っぽく、いくつかのコントロールポイントの座標を元にメッシュを定義するクラス BezierPatch 。Away3DのコミッターGreg Caldwell氏のblogで、チュートリアルとデータ定義用簡易エディタ「Patch Explorer」が公開されてて、やっと使い方が解った。
Away3D Bezier Patch Tutorial

エディタ無しじゃ狙った形作るのキビしすぎだと思う。3Dアプリケーションでサブサーフェスをポリゴン化せずにインポートできるようにしようとしてるのかなぁ。

上記のデモは、16個のコントロールポイント(黄色い四角)でメッシュを定義して、それを鏡面コピーしたもので構成。コントロールポイントを適当に動かすことでメッシュが変形する。有機的に変形できるのが強みですな。


PV3D専用だと思ってあんまりチェックしてなかったJigLibFlashが、PV3D以外のエンジンにも対応(現在、PV3D、Away3D、Sandyに対応)してくれたので、Away3Dでもそのまま使えるようになった。JigLib Flash for Away3D おつかれさま。

これまでのバージョンだと、表示用のメッシュにDisplayObject3Dをダイレクトに指定する形になっていたのが、ISkin3D っていうインターフェースをカマせることで、組み合わせたいエンジンのメッシュを指定できるようになった。
Away3Dを使うなら、

var box:JBox = new JBox(new Away3dMesh(daeobj),100,200,50);
※daeobjは、外部から読み込んだColladaモデルと仮定

って感じ。ちなみに、Away3dMeshクラスの実装は、Mesh オブジェクトを指定するようになってたけど、オレ的に不便なので、Object3D を指定するようにして使ってる。

あと、

var physics:Away3DPhysics = new Away3DPhysics(viewport, 1);
var sphere:RigidBody = physics.createSphere({radius:30, segmentsW:6, segmentsH:6});

なんて感じで、プリミティブ形状については表示用メッシュと物理演算用メッシュを一度に定義できるメソッドが用意された。引数の与え方がPV3DとAway3Dで異なる部分は吸収してくれないので、それぞれのエンジンのルールに従うと。

このあたり、ある程度の説明が google code のプロジェクトページ内Wikiに記載されているので参考になる。
New_API_Very_Short_Tutorial

また、svnリポジトリにもPV3D、Away3Dそれぞれのサンプルがある。これまでとシンタックスが大分異なる(physics.Integrate(0.1); → physics.step(); とか)ので、早めに乗り換えるが吉かと。
コリジョン用の形状にカプセル型が追加されたのがウレシげだなぁ。元祖C++用のJigLibには、trianglemesh や heightmap なんてのもあるので、だんだん移植されるのかも。

早速インチキドライブシミュレータもどきに組み込んでみたんだけど、パラメータ共通なのに挙動が同じにならない。けど、なんか新しいほうがパラメータが正しく反映されてるような気がする。


Drive Test 03(要:FlashPlayer9)

[←] [→] キー … ハンドル
[z] キー … アクセル
[x] キー … ブレーキ
[↓] … バック

遅々たる歩みで進行中。
低コストでZソート補正する screenZOffset を適用。
こんなのがあるの今まで知らなかった。こういう地味な機能がいっぱいあるのがAway3Dのいいところ。

objA.screenZOffset = -1000;
objB.screenZOffset = 500;
※objA,Bは、任意の Object3D オブジェクト。

値が小さいほど他より手前にあるものとして描画される。
Zソート時のインデックスに強制介入というローテク。ローテク最高。

これのおかげで、負荷をほとんど増やさずに車の影を追加できた。ただの黒っぽいポリゴンだけど、影がつくと印象がスゲー変わるなぁ。車が横転すると影も一緒に横転するのはご愛敬。


Drive Test 02(要:FlashPlayer9)

[←] [→] キー … ハンドル
[z] キー … アクセル
[x] キー … ブレーキ
[↓] … バック

まともな挙動にするには JigLib の vehicles パッケージにかなり手を入れなくちゃならなさげ。
パラメータ調整でどこまでいけるか模索中。
ブレーキが機能してなかったんでちゃんと実装。ブレーキランプも光らせた。JigLibのSetHBrakeメソッドって0.5以上の数値を与えないと意味無くて、しかも0.5を超えた途端にホイールをロックするハンドブレーキっぽいものだった。

前回のデモと違ってグリップをズルズル気味にしたので、ケツが滑るタイミングに上手く合わせてカウンタ当てると、なんとなくドリフトっぽい動きが見られていい感じ。キーボードだとシビアだけど決まるとキモチいいー。

確認用に投げやりな三角コーナーを設置してみたけど、バウンディングボックスだと天地がひっくり返ったりして上手くない。うーむ。
道は果てしなく長い。