2009.04 Monthly archives

o3d first impression

2009.04.28

o3dで遊んでみたメモ。
Developer’s Guideサンプルを参考に、PV3D初挑戦の時と同じくCubeを回した。simple.jsっていうPV3DのBasicViewみたいなモジュール使うと楽ができる。

Hello o3d(要:o3dプラグイン)

※デモを見るには、o3dプラグインが、ブラウザにインストールされている必要があるのでそこんとこよろしくです。また、動作ビデオカードに制限があるので、動かない場合もあるです。

オレの中で理解度のベンチマークになってしまいつつある、ネギ振りもやってみた。

ハイエンド ネギ振り(要:o3dプラグイン)
※クリックで振る

もともとPV3D用だったので、スペキュラとかディフューズの設定がデタラメな為、妙に光っております。o3dで外部シーンファイルを読み込んで使うには、専用のコンバータをかます必要がある。DAEファイルをコンバータにかけると、.tgzファイルが出来上がる。中身はテクスチャ用のビットマップデータとか頂点バッファ?とかのバイナリとか。元のColladaは、XMLから.jsonに変換された。.jsonのほうがパースが速いのかね?
この辺の解説は、Developer’s GuideのImporting Scene Filesセクションに書いてある。

つまずいたのはサーバーにアップロードした際、同一ドメイン内だとデータが読み込まれなかったこと。フォーラムに同じ問題に当たった人がいて、この人は拡張子を「.tgz」から任意の適当な拡張子に変えることで解決できてたんだけど、オレの場合はだめだった。読み込み元となる.htmlとは別のサーバにアップロードした上で読み込んだら上手くいった。っていうかセキュリティ的に大問題だろこれ。

追記:
MIMEタイプ設定の問題だった。.htaccessに以下を追加で解消。

AddType application/x-gzip .tgz

まぁもう、まだまだドラフト版といった感じ。参照先を間違えたりするといきなりブラウザごと落ちたりとか普通だし。普及とかどうとか以前の問題ですな。

そういえば、o3dの登場でオレ的に注目度が上がったのが JSAway。Away3DのJS版なんだけど、これのエンジン部分をo3dに丸投げするようになってくれたら超使い易い。一気にo3dがお手軽になるんだけどなー、やらないだろうな。

[PV3D2.0] attitudeControl

2009.04.27

attitude Control(要:FlashPlayer9)

haramakotoさんのエントリー「方向ベクトルからMatrix回転への変換」を読んで。

ある方向に形状の任意軸ベクトルを揃えることで、姿勢を制御するというお題。
haramakotoさんはクォータニオンで挑戦されているようなので、オレは行列でやってみた。正直言ってオレもよくわかってないんだけど、なんとなく上手くいったので書いておく。

例えば、ある形状のY軸を適当な方向に向けたい場合の手順は

  1. 姿勢をコントロールしたいオブジェクトのY軸ベクトルをゲット — [1]元の姿勢
  2. ゲットしたY軸のベクトルに目標座標のベクトルを加算して新しいベクトルをゲット — [2]変形後の姿勢
  3. [1]と[2]の外積をとって、法線ベクトル(回転軸)をゲット — [3]
  4. 二つのベクトルの角度をゲット — [4]
  5. [3]のベクトルと、[4]の角度でもって回転行列を作る
  6. 姿勢をコントロールしたいオブジェクトのtransformと合成

って感じで上手くいった。

姿勢をコントロールしたいオブジェクトのY軸ベクトルは、DisplayObject3Dのtransformプロパティを参照すれば、調べることができる。

n11 n12 n13 n14
n21 n22 n23 n24
n31 n32 n33 n34
n41 n42 n43 n44

transformプロパティ(Matrix3D)の各列、
|n11,n21,n31|、|n12,n22,n32|、|n13,n23,n33|は、形状のX,Y,Z軸の単位ベクトルを示してる。なので、ある形状Obj(DisplayObject3D)の各軸のベクトルは、

X軸:Xvec:Number3D = new Number3D(Obj.transform.n11, Obj.transform.n21, Obj.transform.n31);
Y軸:Yvec:Number3D = new Number3D(Obj.transform.n12, Obj.transform.n22, Obj.transform.n32);
Z軸:Zvec:Number3D = new Number3D(Obj.transform.n13, Obj.transform.n23, Obj.transform.n33);

として取り出すことができる。これで、ある時点での形状の姿勢がわかる。ここまではyamasvさんの「回転行列の列ベクトル」の解説が詳しい。

「ゲットしたY軸のベクトルに目標座標のベクトルを加算」ってのは、自分も理解できてない。感覚的にこうじゃねーの?とかいうドンブリ勘定実装したら上手くいっちゃったので説明できない。わっはっは。…申し訳ございません。最初は目標座標のベクトルを加算せず、目標座標のベクトルをそのまま使ったんだけど、それだとリボン状にPlaneが並ばなかった。

残りの手順は、[Papervision3D2.0] Quaternionと同じ要領で、最終的に姿勢をコントロールするコードは以下のような感じになった。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//ある方向に向けたい軸
var StartVec:Number3D = new Number3D( obj.transform.n12, obj.transform.n22, obj.transform.n32 );
 
//目標になる座標
var TargetPoint:Number3D = new Number3D( 目標のX座標, 目標のY座標, 目標のZ座標 );
 
//元のベクトルを揃わせたい最終的なベクトル
var EndVec:Number3D = Number3D.add( StartVec, TargetPoint );
StartVec.normalize();
EndVec.normalize();
 
//2つのベクトルの法線ベクトル(回転軸)
var vcross:Number3D = Number3D.cross( EndVec, StartVec );
vcross.normalize();
//2つのベクトルから回転角度(ラジアン)算出
var rot:Number = Math.acos(Number3D.dot( StartVec, EndVec));
 
//ベクトルを合わせるための回転軸と角度から回転行列作成
var mat:Matrix3D = Matrix3D.rotationMatrix(vcross.x, vcross.y, vcross.z, rot);
 
//行列を適用
obj.transform = Matrix3D.multiply3x3(mat, obj.transform);

あと、wonderflに上がってたharamakotoさんのコードをforkして、行列での姿勢制御版を作ってみた。
ここらへん、ちゃんと理解したいなぁ。

[Away3D] Away3D 2.3 Feature – BezierPatch

2009.04.23

BezierPatch Test(要:FlashPlayer9)

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

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

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

O3D

2009.04.22

O3D API だって。Native Client派生かなぁ。

プラグインをインストールすると、HTML内に書いたJavaScriptで3Dコンテンツが動く。実行時にコンパイルしてるのか。シーンファイルとしてはColladaが読める。[2009.04.27追記]ドキュメント斜め読みしてウソ書いてた。いまのところColladaファイルを専用のコンバータ使って、o3d用のファイルに変換したものしかシーンファイルには使えない模様。
んで、GPUサポートしてて、シェーダー(HLSLとCgで書けるようですな)も使える。素晴らしくイマドキですな。メタセコビューアとかは、こっちで作ればほぼ完全な状態で表示できそう。
サンプルもいっぱいあるので参考になる。

ハードウェア依存が高いのを承知で使えば、ハイエンド環境で勉強できていいと思う。ただ、コードが丸見えってのと、リソースの保護が難しそうなので、仕事となると話が変わってくるな。

とりあえず、これを見てヤケをおこしたAlternativaPlatformが、Alternativa3Dをオープン化してくれることに期待。

[PV3D2.0] virtualMouse axis on DAE

2009.04.21

Paint to DAE mash(要:FlashPlayer9)

WakaZさんのエントリー「PV3D | DAEファイルの読込み」

DAEのオブジェクトにはVirtualMouseは効かないようで(あたりまえ)・・・。

を読んで。

これは確かに解りにくいなー。
例えば、

var obj_dae:DAE = new DAE();
obj_dae.load("hoge.dae");
scene.addChild(obj_dae);
obj_dae.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, mouseClick);

だとイベントが取れない。なんでか?

PV3Dとかだと、3Dアプリケーションで作った形状データを取り込む目的でCollada形式を使ってるけど、本来、ColladaってシーングラフをまるごとXMLで記述しようっていうフォーマット(だと思う)なので、メッシュ以外にもカメラとかライトなど、3Dアプリ上で定義した全要素を定義できる。いってみれば、

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

で生成されたdaeObjってPV3Dのシーンみたいなもんで、ツリー構造なDisplayObject3Dの塊。なので、daeObj内にある目的のメッシュを明示的に参照しないとダメなのですな。あと、InteractiveScene3DEventを有効にする場合のお約束、マテリアルのinteractiveプロパティをtrueにするのを忘れずに。

具体的には、

vMouse = viewport.interactiveSceneManager.virtualMouse;
Mouse3D.enabled = true;

-- 中略 --

obj_dae = new DAE();
obj_dae.load( "hoge.dae" );
obj_dae.addEventListener( FileLoadEvent.LOAD_COMPLETE, compCollada );
scene.addChild(obj_dae);

function compCollada(event:Event):void
{
    //対象となるマテリアルのinteractiveプロパティをtrueに
    obj_dae.materials.getMaterialByName("ターゲットのマテリアル").interactive = true;

    //対象メッシュの参照
    var dae_rootNode:DisplayObject3D = obj_dae.getChildByName("COLLADA_Scene");
    var targetMesh:DisplayObject3D = dae_rootNode.getChildByName("ターゲットの名前");

    //対象メッシュにリスナ設定
    targetMesh.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, mouseClick);
}

private function mouseClick(e:InteractiveScene3DEvent):void
{
    var axis:Point = new Point(vMouse.x, vMouse.y);
    trace(axis.x + " / " + axis.y);
}

って感じで。

上記で言うところの obj_dae内部では「COLLADA_Scene」って名前のDisplayObject3Dをルートとした階層構造(シーングラフ)ができあがってるってのを意識することが重要と。

getChildByNameで指定する名前は、ルートである「COLLADA_Scene」は固定、COLLADA_Scene以下の各メッシュは3Dアプリで付けた名前がエクスポートされてるはず。自分で作ったdaeファイルじゃない場合は、

var dae_rootNode:DisplayObject3D = obj_dae.getChildByName("COLLADA_Scene");
dae_rootNode.childrenList();

で一覧が取れる。

ということで出来上がったのが上記のデモ。
DAEでもプリミティブと同じようにペイントできるよ。