久々の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 >> 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 できるようにしておくとか、負荷軽減の工夫は必要かと。