note.x

Floor Collision Demo(要:FlashPlayer9)※マウスクリックでSphereが増えます。

衝突判定のお勉強。とにかく頭に来るほど基礎が無いので、先人の技から学んでいくぞと。

ベースにしたのは zero point nine のデモコード。エッセンスのみ抽出させていただいて、キー入力によるコントロールとかはひとまず無しにしてみた。あとオリジナルには無い要素として、perlinNoiseのoffsetを使った連続的な変形エフェクトを追加した。変な弄り方したのが悪いのか、ときどき床をすり抜けてSphereがこぼれ落ちる;

オブジェクト中心座標のX,Z成分から、Planeの該当Faceを割り出した後、Faceを構成する2つのポリゴンのうち、どっちにオブジェクト中心座標のX,Z成分が含まれるかを求め、オブジェクト中心座標から-Y方向に伸びるレイと、△ポリの交点を求めて、最終的なY座標を得る。ってな方法だと思うので、地面がPlane以外だとこの方法は使えなさそう。

zero point nine のコードと、「マルペケつくろーどっとこむ」さんの記事を比較しながらアルゴリズムを紐解こうとしたんだけど、オブジェクト中心座標から-Y方向に伸びるレイと、△ポリの交点を求める部分がどーもよくわかんね。このへん色んな方法があるんだろうけど、zero point nine のそれはなんだか面倒なことをやってそうな印象。うーむ。

とりあえずは、これを突破口に内積と外積の海に飛び込んでみよう。

オレのような凡人が3Dプログラミングの勉強する場合、ある程度期待した通りの動きをするコードがとりあえず無いことには、正直とっかかりが掴めない。そういった意味で『ニワトリが先か卵が先か』みたいな状況にしょっちゅう陥るわけで、ヒントになるコードを公開してくれてる方々には感謝しきれねーッス。本当にありがたいことでございます。


レディオヘッドのアルバムIn Rainbows収録曲”House of Cards” のPVで使われた3DキャプチャデータのcsvファイルがGoogle Codeで公開された。データと一緒にProce55ingのソースも公開されてて太っ腹。

RA DIOHEA_D / HOU SE OF_C ARDS – Google Code

ってのを、PV3Dのオフィシャルブログで見かけて、しかもなんか件のページのFlashビューアが、PV3Dを多少モディファイで作られたらしいので、Away3Dでも出来ないもんかなと思った次第。もうすでにこれ使ったデモで溢れ返ってそうな気がするけど、やるだけやってみた。

House of Cards 3D Plot(要:FlashPlayer9)

12000頂点、とりあえずプロットまではできた。激重だけどなんとか動いてる。
元ネタのFlashビューアは、ブラーフィルターがかかってる上にそこそこ軽いのが不思議でしょーがねぇ。頂点間引いてるのかなぁ?描画に関してはビットマップ化がキモなんじゃねーかとにらんでガリガリやってみたけど、どーも上手くいかね。あとはCSV→プロットまでのパイプラインを最適化しないと、ギリギリな感じの今の状態じゃアニメーションなんかとても無理だ。

むむむー、悔しいからもう少し粘ってみよう。
せめてブラーフィルタかけてもそこそこ動いてる状態にはもっていきたい。

データをスクリーン上に再構成するのも面白いんだけど、このデータ自体を作るほうにも興味津々。

人物のキャプチャには、
http://www.geometricinformatics.com/geoVideo.php
町並みのキャプチャには
http://www.velodyne.com/lidar/

の技術をそれぞれ使ったっつーことなんだけど、サッパリわからん。とりあえず金はかかりそうだ(笑)
特に後者のほうは、軍事転用前提のロボットカーレースで使われてるような技術とか入ってそうで高そうな印象。なんだろ「ものすごいレーザー測量器」って感じなのかな。
トイレくらいの広さしかスキャンできないオモチャっぽいものでもいいから個人で買えるものがあったらいいのに。タカラトミーが作ってくれることに期待。

それにしたってデータ大公開ってRadioHeadスゲー。アルバムの売り方といい、こういうプロモーションって日本でもやればいいのにな。せっかくニコニコとかあるんだし。

08/07/23 追記:
12000頂点をAway3Dのジオメトリパイプラインで処理するってのは、どー考えても自前で透視変換した座標をビットマップ描画する方法には速度的に及ばないってのをNUTSUさんや、MASDAさんのデモ見て痛感した。特にMASDAさんのはサウンドスペクトルになってて超カコイイ!

んで、自前レンダリングの軽さとAway3Dの汎用性の良いトコ取りを目指して、カメラと空間のみを拝借(ビュー行列で座標変換。プロジェクション変換してないので微妙にインチキ)しつつ、描画は自前で行う方法を模索してみた。

bitmapTest.swf(要:FlashPlayer9)

結局重いんだけど、エフェクト追加したわりには軽くなったかも。

いずれにせよ、ビットマップにしちゃえばテクスチャとして使えるぜーってことで、闇雲に貼ってみたのが下のデモ。オレはいったい何がしたいのか。

textureDemo.swf(要:FlashPlayer9)


たまには3D以外のこともやってみるべーということで、NUTSUさん作の、Processing気分を味わうAS3.0ライブリラリ「Frocessing」で遊んでみた。

nutsu/Frocessing – Spark project

svnリポジトリに上がってるサンプルが .flaファイルしか無いので、FlexSDKで扱う方法を模索。とりあえず F5MovieClip2DとかF5MovieClip3Dを継承しとけばいいみたいだということにオレの中で結論づいたので、なんぞ作ってみる。

で、これまたP5ライクな「NodeBox」のサンプルを移植して若干イジったのが下記。
Hello Froce55ing(要:FlashPlayer9)

package
{
    import flash.display.*;
    import frocessing.display.F5MovieClip2D;
 
    public class main extends F5MovieClip2D
    {
        private var count:int;
 
        public function main()
        {
            stage.frameRate = 30;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
 
            super();
            QBest();
        }
 
        public function draw():void
        {
            clear();
            background(255,255,255);
 
            count++;
            translate(fg.width/2, fg.height/2);
            fill(0, 153, 230, 0.1);
            stroke(0, 153, 230, 0.4);
            strokeWeight(0);
 
            for (var i:int = 0; i < 150; i++)
            {
                var x:Number = random(fg.width/2, -fg.width/2) * cos(radians(count));
                var y:Number = random(fg.height/2, -fg.height/2) * cos(radians(count));
                var r:Number = cos(radians(count)) * 50;
                circle(x,y,r);
            }
        }
 
    }
}

ステージプロパティ設定してるあたりがかなりビミョーだけど、こんな感じでいいのかなぁ?FlashIDEのドキュメントクラス設定と同等のことってFlexSDKで出来るのかしら。このあたり激しく勉強不足でワカンネー。けど動いたからまぁいいや(えー)

もっとP5っぽいものを、と思ってストレンジアトラクタ系ネタも作ってみたものの、lineで描画しまくったら予想通りのブラクラになっちゃったんで静止画のみで。

setBmpMode()を使えばBitmapが描画対象になる…のか?軽くする方法が解かるまでひとまず保留っと。

FlexSDK3 + Rascut + mi という修行僧のような環境でもそこそこ楽しめた。
でもまぁ素直にFlashIDEつかってフレームスクリプト書いたほうがシンプルに書けて、よりP5気分に浸れるのかも。ただこれ、慣れちゃうとAS3.0本来のシンタックス忘れるわ、間違いなく確実にオレは(笑)


久々のPV3Dネタ。
アクセスログ見てたら、FxUGのフォーラムでオレのエントリが引き合いに出されてたんで、これについて補足してみる。

とりあえず、

var bitmapMaterial:BitmapMaterial = new BitmapFileMaterial("sample.jpg");

は、マズいんじゃないかと。

それはおいといて、上記のトピックは「 MovieMaterial を使えば、precise が有効にできる。」という解釈で終了してる感じだけど、そもそも precise プロパティで制御するパースペクティブコレクトの機能は、BitmapMaterial クラスで提供されてるものなので BitmapMaterial を継承する全マテリアルで指定が可能なハズ。MovieMaterial も、そのひとつっていうだけのハナシ。
というわけで、基本である BitmapMaterial で precise プロパティを使ってみたデモが下記。

material.preciseのテスト(要:FlashPlayer9)
※キューブをクリックすると、preciseプロパティの true / false 切り替え。

そのソース。

package
{
    import flash.display.*;
    import flash.events.*;
 
    import org.papervision3d.view.BasicView;
    import org.papervision3d.events.InteractiveScene3DEvent;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
 
    [SWF(backgroundColor=0x000000)]
 
    public class bm_precise extends BasicView
    {
        private var rootNode: DisplayObject3D;
        private var valx    : Number = 0;
 
        [Embed(source='texture.jpg')] public var texBitmap:Class;
 
        public function bm_precise()
        {
            stage.frameRate = 30;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            //viewportの定義とカメラタイプ定義
            super (0,0,true,true,"Target");
            init3D();
        }
 
        public function init3D():void
        {
            rootNode = new DisplayObject3D();
            scene.addChild(rootNode);
 
            //カメラ設定
            camera.z = -1200;
            camera.y = 500;
            camera.fov = 30;
 
            //マテリアル設定
            var material:BitmapMaterial = new BitmapMaterial( new texBitmap().bitmapData, true );
            material.interactive = true;
            var materials:MaterialsList = new MaterialsList(
            {
                all : material
            });
 
            //オブジェクト生成
            var objCube:Cube = new Cube( materials, 250, 250, 250, 1, 1, 1);
            rootNode.addChild(objCube);
            objCube.material.tiled = true;
            objCube.material.maxU = 2;
            objCube.material.maxV = 2;
 
            objCube.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, mouseRelease);
 
            //レンダリング開始
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            valx += (stage.mouseX - (stage.stageWidth &gt;&gt; 1 ) ) / 50;
            rootNode.rotationY = valx;
 
            super.onRenderTick(event);
        }
 
        private function mouseRelease(event:InteractiveScene3DEvent):void
        {
            var targetMat:BitmapMaterial = event.displayObject3D.materials.materialsByName['all'];
            targetMat.precise = !targetMat.precise;
        }
    }
}

SWFにBitmapを埋め込んじゃうのが一番ラク。
件のトピックで質問者の方が公開してるソースで使ってる BitmapFileMaterial のように、外部ファイルを使ってマテリアルを生成する場合は、ファイルの読み込み完了を待たずに描画を始めるとエラーになる。これは、BitmapMaterial の drawTriangle メソッド内で precise プロパティが false の場合は bitmap の存在をチェックしているのに、trueの場合、bitmap が nullでも描画しようとしてるため。バグか仕様かはともかくとして、要はファイルの読み込み完了を待たないとダメと。

で、どうするかの例が下記のソース。

package
{
    import flash.display.*;
    import flash.events.*;
 
    import org.papervision3d.view.BasicView;
    import org.papervision3d.events.InteractiveScene3DEvent;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.materials.BitmapFileMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
 
    [SWF(backgroundColor=0x000000)]
 
    public class bmf_precise extends BasicView
    {
        private var rootNode:DisplayObject3D;
        private var material:BitmapFileMaterial;
        private var valx    : Number = 0;
 
        public function bmf_precise()
        {
            stage.frameRate = 30;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            //viewportの定義とカメラタイプ定義
            super (0,0,true,true,"Target");
            loadMaterial();
        }
 
        public function loadMaterial():void
        {
            //ファイル読み込み後のコールバック指定
            BitmapFileMaterial.callback = init3D;
            material = new BitmapFileMaterial( 'texture.jpg', true );
            material.interactive = true;
        }
 
        public function init3D():void
        {
            rootNode = new DisplayObject3D();
            scene.addChild(rootNode);
 
            //カメラ設定
            camera.z = -1200;
            camera.y = 500;
            camera.fov = 30;
 
            //マテリアル設定
            var materials:MaterialsList = new MaterialsList(
            {
                all : material
            });
 
            //オブジェクト生成
            var objCube:Cube = new Cube( materials, 250, 250, 250, 1, 1, 1);
            rootNode.addChild(objCube);
            objCube.material.tiled = true;
            objCube.material.maxU = 2;
            objCube.material.maxV = 2;
 
            objCube.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, mouseRelease);
 
            //レンダリング開始
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            valx += (stage.mouseX - (stage.stageWidth >> 1 ) ) / 50;
            rootNode.rotationY = valx;
 
            super.onRenderTick(event);
        }
 
        private function mouseRelease(event:InteractiveScene3DEvent):void
        {
            var targetMat:BitmapFileMaterial = event.displayObject3D.materials.materialsByName['all'];
            targetMat.precise = !targetMat.precise;
        }
    }
}
BitmapFileMaterial.callback = init3D;

って感じで、ファイル読み込み後のコールバックが指定できるので、これを使った。PV3Dの仕様に合わせるならこんな感じだけど、実際にはBitmapFileMaterialを使わずに自前で外部ファイル読み込んで、BitmapMaterial使ったほうが自由度高いと思う。MovieMaterialを使う場合でも、外部のSWFを読み込んで使うなら当然同じ事に注意。

あと、オレのようにFlexSDKオンリーで外部ファイルを使うコードは、コンパイル時

mxmlc -use-network=false hoge.as

にしないと、スタンドアローンプレーヤーでローカル再生できない。FlashIDEとかFlexBuilderだったら、この辺は大丈夫なのかな。(Rascutはこういうことから開放されるので、すこぶる便利。)

と、ここまで書いてアレだけど、上記ソースのカメラ設定部分を下記のように書き換えると、案外歪みが目立たなくなる。

//カメラ設定
camera.z = -3500;
camera.y = 1500;
camera.focus = 320;
camera.zoom = 10;

テクスチャの歪みはパースがきつくなると発生するので、視野角が極端に広くならないようにfocusとzoomを調整してやれば、それで済んじゃう場合もある。奥行き感が無くなるのとトレードオフだけど、今回のように「ステージ中心にオブジェクトひとつ」みたいな場合はこれで充分かも。precise 使う場合でも、カメラとの距離に応じて true / false できるようにしておくとか、負荷軽減の工夫は必要かと。


SCRATCHBRAINさんのエントリ「Away3D v2.1.0の使い方(8)- daeファイルのエラー解析中」で、オレがハマったのと全く同じ問題でお悩みのご様子なので支援してみる。

結論から書くと、書き出された.daeファイルにUV定義に関するノードが含まれてないのが原因。…たぶん。
他の問題の可能性もあるけど、SCRATCHBRAINさんが公開して下さった 問題のあるdaeファイル を拝見した感じだとほぼ間違いないかと。

解決するには、以下のような感じで。

1. UVを定義するオブジェクトを選ぶ。
2. Editingパネルの「Mesh」にある、「UV Texture」項目の「New」ボタンを押してUV定義を新規作成しつつ割り当て。オブジェクトが複数ある場合は、全てにこの作業を行う。(Blender2.45までだったら、UV Face Selectモードに一度切り替えることで、自動でUV定義される)

3. Collada Pluginでエクスポート。

キチンと作り込む場合は、UV定義しただけじゃ当然ダメで、UVエディット機能でしっかり指定する必要があるけど、単純にAway3DやPV3Dで読み込み可能なファイルにするだけなら、上記の手順でイケるはずです。(ダメだったら、イケねーぞゴルァ!とかコメントでも)

それから、(ほとんど私信になっちゃうけど)件の.daeファイルにはカメラやライト、Physicsに関するノードが含まれてますが、これ全然いらないです。書き出したいオブジェクトのみを選択した状態で、「Only Export Selection」を有効にしてエクスポートするといいと思います。あと「Disable Physics」を有効にするといいんじゃないかと。これらは、Away3Dで読み込むこととはあんまり関係ないですが、余計なデータは無いに越したことはないので。


AS3Exporterのテスト(要:FlashPlayer9)

Blenderから頂点・頂点インデックス・UV座標値を、各種AS3用3Dエンジン(Away3D、PV3D、Sandy)に対応した.asファイルとして吐き出すスクリプトが公開されてたのを最近見つけたんで使ってみた。スクリプト本体のダウンロードや、導入方法、基本的な使い方は、下記配布元で懇切丁寧に解説されてる。
http://rozengain.com/?postid=54

Away3D2.0以降で使う場合は、パッケージ構造の変更に対応するために下記のような修正が必要だった。

AS3Export.py – 187〜199行目

	as_output_string = 		"package "+as_package_name.val+" {\n"
	as_output_string +=		"\timport away3d.core.*;\n"
	as_output_string +=		"\timport away3d.core.mesh.*;\n"
	as_output_string +=		"\timport away3d.core.material.*;\n"
	as_output_string +=		"\timport away3d.core.utils.*;\n\n"

	as_output_string +=		"\tpublic class "+as_class_name.val+" extends Mesh {\n"
	as_output_string +=		"\t\tprivate var ve:Array;\n"
	as_output_string +=		"\t\tprivate var fa:Array;\n\n"
	as_output_string +=		"\t\tpublic function "+as_class_name.val+"(init:Object = null) {\n"
	as_output_string +=		"\t\t\tsuper( init );\n"
	as_output_string +=		"\t\t\tinit = Init.parse(init);\n"
	as_output_string +=		"\t\t\tve = [];\n"

を、下記のように変更した。

	as_output_string = 		"package "+as_package_name.val+" {\n"
	as_output_string +=		"\timport away3d.core.*;\n"
	as_output_string +=		"\timport away3d.core.base.*;\n\n"

	as_output_string +=		"\tpublic class "+as_class_name.val+" extends Mesh {\n"
	as_output_string +=		"\t\tprivate var ve:Array;\n"
	as_output_string +=		"\t\tprivate var fa:Array;\n\n"
	as_output_string +=		"\t\tpublic function "+as_class_name.val+"(init:Object = null) {\n"
	as_output_string +=		"\t\t\tsuper( init );\n"
	as_output_string +=		"\t\t\tve = [];\n"

それ以外は今のとこ問題なく、最新の2.46でもちゃんと動作した。
忘れがちなので要注意だったのが、Colladaエクスポーターと違って△ポリに自動変換してくれないので、エクスポート前に△ポリに変換しておくべし。

書き出された .as は、普通にMeshを継承したクラスになってるので、プリミティブオブジェクトと同じように扱えばオッケ。
当然ながらアニメーションには対応してないけど、これ結構便利かも。頂点とUV定義だけで充分な場合Collada使う意味無いし、そういうケースのほうが多いし。あと、モデルデータがファイルとして存在しない分コンテンツ保護にも繋がりそう。

追記:
ライブラリの更新にExporter側が追いついてないみたいで、書き出した形状の法線が逆になる。テクスチャ貼らないならライブラリ側で反転させればいい(Away3DならinvertFaces()、PV3Dならoppositeプロパティ)けど、テクスチャ貼る場合はそうもいかないのでAS3Exporterのテンプレートに手を加える。

AS3ExpAway3D220.as 21行目

ve.push(new Vertex(x, y, z));
↓
ve.push(new Vertex(x, z, y));

AS3ExpPapervision20.as 22行目

ve.push(new Vertex3D(x, y, z));
↓
ve.push(new Vertex3D(x, z, y));

と、頂点の定義順を入れ替えるだけ。あとは「Away3D 2.2, 2.3, 2.4, 3.4」「Papervision2.0」用としてエクスポート。Away3D 3.6、PV3D 2.1 で動作確認した。他のライブラリは未確認だけど問題があった場合の対処についての理屈は同じ。