3Dの座標を2Dの座標に変換
DisplayObjectのlocal3DToGlobalを使わずに、PerspectiveProjectionの情報と、
回転しているオブジェクトのMatrix3Dから4隅の2D座標を取得して、マーカーを追従させています。
デモ用に表示オブジェクトを作成していますが、なくても計算可能なので、
オブジェクトをそもそも描画するかしないかの判定とかに広く使えます。
実際に自身でFlexで3Dカルーセルを作るのに使ったロジックです。
データがいくら有っても、見える範囲しか描画オブジェクトを生成しないようにするために使っています。
投影変換まわりの処理では、インスタンスを新たに作らない方法にて省メモリ化・高速化を行なっていますが、
もっといい方法があったら教えて下さい。
package
{
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Point;
import flash.geom.Utils3D;
import flash.geom.Vector3D;
import flash.utils.getTimer;
[SWF(frameRate=60, width=465, height=465, backgroundColor=0xffffff)]
public class FlashTest extends Sprite
{
private var _marker0:Shape;
private var _marker1:Shape;
private var _marker2:Shape;
private var _marker3:Shape;
private var _projectedVerts:Vector.<Number>;
private var _projection:PerspectiveProjection;
private var _projectionMatrix:Matrix3D;
private var _shape:Shape;
private var _shapeMatrix:Matrix3D;
private var _start:uint;
private var _tempMatrix:Matrix3D;
private var _tempVector:Vector3D;
private var _uvts:Vector.<Number>;
private var _vertsIn:Vector.<Number>;
private var _vertsOut:Vector.<Number>;
public function FlashTest()
{
//シェイプを作成
_shape = new Shape();
var g:Graphics = _shape.graphics;
g.beginFill(0x777777);
g.drawRect(0, 0, 300, 300);
g.endFill();
//シェイプを3Dモードにしておく
_shapeMatrix = _shape.transform.matrix3D = new Matrix3D();
addChild(_shape);
//マーカ作成
_marker0 = new Marker();
_marker1 = new Marker();
_marker2 = new Marker();
_marker3 = new Marker();
addChild(_marker0);
addChild(_marker1);
addChild(_marker2);
addChild(_marker3);
//プロジェクション設定
_projection = new PerspectiveProjection();
_projectionMatrix = _projection.toMatrix3D();
transform.perspectiveProjection = _projection;
//マウスの位置に消失点を設定する
stage.addEventListener(MouseEvent.MOUSE_MOVE, updateProjectionCenter);
//回転処理の準備
_uvts = new Vector.<Number>();
_vertsIn = new Vector.<Number>();
_vertsOut = new Vector.<Number>();
_projectedVerts = new Vector.<Number>();
_tempMatrix = new Matrix3D();
_tempVector = new Vector3D();
_start = getTimer();
//座標の設定(シェイプの4隅の座標)
_vertsIn[0] = 0;
_vertsIn[1] = 0;
_vertsIn[2] = 0;
_vertsIn[3] = 300;
_vertsIn[4] = 0;
_vertsIn[5] = 0;
_vertsIn[6] = 0;
_vertsIn[7] = 300;
_vertsIn[8] = 0;
_vertsIn[9] = 300;
_vertsIn[10] = 300;
_vertsIn[11] = 0;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(e:Event):void
{
//角度
var rotation:Number = ((getTimer() - _start) % 2000 / 2000) * 360;
//シェイプを中心で適当に回転
_shapeMatrix.identity();
_shapeMatrix.appendTranslation(-150, -150, 0);
_shapeMatrix.appendRotation(rotation, Vector3D.X_AXIS);
_shapeMatrix.appendRotation(rotation, Vector3D.Z_AXIS);
_shapeMatrix.appendRotation(rotation, Vector3D.Y_AXIS);
_shapeMatrix.appendTranslation(232.5, 232.5, 0);
//マーカーの位置を追従
//マトリクスの設定
var center:Point = _projection.projectionCenter;
_tempMatrix.identity();
_tempMatrix.appendTranslation(-center.x, -center.y, _projection.focalLength);
_tempMatrix.append(_projectionMatrix);
//投影変換
_shapeMatrix.transformVectors(_vertsIn, _vertsOut);
Utils3D.projectVectors(_tempMatrix, _vertsOut, _projectedVerts, _uvts);
//マーカーの位置を設定
_marker0.x = _projectedVerts[0] + center.x;
_marker0.y = _projectedVerts[1] + center.y;
_marker1.x = _projectedVerts[2] + center.x;
_marker1.y = _projectedVerts[3] + center.y;
_marker2.x = _projectedVerts[4] + center.x;
_marker2.y = _projectedVerts[5] + center.y;
_marker3.x = _projectedVerts[6] + center.x;
_marker3.y = _projectedVerts[7] + center.y;
}
//消失点の設定
private function updateProjectionCenter(e:MouseEvent):void
{
_projection.projectionCenter = new Point(e.stageX, e.stageY);
transform.perspectiveProjection = _projection;
}
}
}
import flash.display.Graphics;
import flash.display.Shape;
class Marker extends Shape
{
public function Marker()
{
var g:Graphics = graphics;
g.beginFill(Math.random() * 0x1000000);
g.drawCircle(0, 0, 10);
g.endFill();
}
}