simple 3D 06 (not using PV3D)
PV3Dとかを使わない3D表現その6
画像をFlickrから取ってきて表示しました。
画像をゆがめる方法は以下を参考にしました。
http://www.d-project.com/flex/009_FreeTransform/
Matrix3DとVector3Dは自作。Matrix3Dはちょっと改造しました。
やればやるほどPV3Dなど3Dライブラリの凄さを実感します。
/**
* Copyright sakef ( http://wonderfl.net/user/sakef )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/tpsj
*/
/*
PV3Dとかを使わない3D表現その6
画像をFlickrから取ってきて表示しました。
画像をゆがめる方法は以下を参考にしました。
http://www.d-project.com/flex/009_FreeTransform/
Matrix3DとVector3Dは自作。Matrix3Dはちょっと改造しました。
やればやるほどPV3Dなど3Dライブラリの凄さを実感します。
*/
package
{
import com.bit101.components.Label;
import net.hires.debug.Stats;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.Security;
[SWF(width="465", height="465", backgroundColor="0xffffff")]
public class simple3d_06 extends Sprite
{
// Planeサイズ・中心・フォーカス等
private static const SIZE:Number = 90;
private static const RADIUS:int = 300;
private static const CENTER_X:int=465 >> 1;
private static const CENTER_Y:int=465 >> 1;
private static const CAMERA_RADIUS:int=600;
private static const FOCUS:Number=400;
// 画像検索に使うキーワード
private static const KEYWORD:String = "sea";
// 中心を示すベクトル・上を示すベクトル
private const CENTER:MyVector3D=new MyVector3D();
private const UP:MyVector3D=new MyVector3D(0, 1, 0);
// カメラ用
private var camera:MyVector3D;
private var isMouseDown:Boolean;
private var oldX:Number;
private var oldY:Number;
private var targetRot:Number;
private var targetPitch:Number;
private var rot:Number;
private var pitch:Number;
// コンテナとPlaneの数
private var container:Array;
private var n_planes:int;
// 画像用
private var img_array:Array;
private var count:int;
private var label:Label;
// コンストラクタ
public function simple3d_06()
{
// ステージ設定・マウス関係停止。
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
stage.quality=StageQuality.HIGH;
stage.frameRate=30;
mouseChildren=false;
mouseEnabled=false;
// Stats追加
addChild(new Stats());
// 変数の初期化
img_array = [];
container = [];
oldX=oldY=rot=pitch=n_planes=count=0;
targetPitch=90;
targetRot=180;
isMouseDown=false;
camera=new MyVector3D(0, 0, FOCUS);
// Planeを球体に並べる
var radian:Number = Math.PI/180;
var H:int=(RADIUS * Math.PI) / SIZE;
var theta1:Number;
var theta2:Number=90;
for(var i:int=0; i < H; i++)
{
theta1=0;
var pn:int=Math.floor((2 * RADIUS * Math.PI * Math.cos(theta2 * radian)) / SIZE);
for(var j:int=0; j < pn; j++)
{
var plane:MyPlane = new MyPlane(SIZE, n_planes);
plane.rotationX=-theta2;
plane.rotationY=theta1;
plane.x=RADIUS * Math.cos(theta2 * radian) * Math.sin(theta1 * radian);
plane.y=RADIUS * Math.sin(theta2 * radian);
plane.z=RADIUS * Math.cos(theta2 * radian) * Math.cos(theta1 * radian);
theta1+=360 / pn;
container[n_planes] = plane;
n_planes ++;
}
theta2-=180 / H;
}
// ロード状況用ラベルの初期化
label = new Label(this, CENTER_X-80, CENTER_Y-50);
label.scaleX = label.scaleY = 2;
label.text="Loading images from flickr.\n" + count.toString() + "\t/ " + n_planes.toString();
// ポリシーファイルの読み込み
Security.loadPolicyFile("http://api.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm1.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm2.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm3.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm4.static.flickr.com/crossdomain.xml");
// Flickrから画像のロード
var api_loader:URLLoader=new URLLoader;
api_loader.addEventListener(Event.COMPLETE, onCompleteAccessAPI);
api_loader.load(new URLRequest("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=25c5f4bc0087edf4d6efdc567daf0a64&tags="+KEYWORD + "&per_page=" + n_planes));
}
// FlickrAPIへのアクセスが成功したら実行する関数
private function onCompleteAccessAPI(e:Event):void
{
var api_loader:URLLoader=e.target as URLLoader;
api_loader.removeEventListener(Event.COMPLETE, onCompleteAccessAPI);
// XMLのパースと画像のURLの作成・ロード
var xml:XML=XML(api_loader.data);
if (xml.@stat == "ok")
{
for each(var photo:XML in xml.photos.photo)
{
var url:String="http://farm" + photo.@farm + ".static.flickr.com/" + photo.@server + "/" + photo.@id + "_" + photo.@secret + ".jpg";
var img_loader:Loader=new Loader();
img_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompletePhotoLoad);
img_loader.load(new URLRequest(url), new LoaderContext(true));
}
}
}
// 画像のロードが終わったら実行する関数
private function onCompletePhotoLoad(e:Event):void
{
var img_loader:Loader=(e.target as LoaderInfo).loader;
img_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onCompletePhotoLoad);
// ロードした画像の表示
img_array[count]=(img_loader.content as Bitmap).bitmapData;
count ++;
label.text="Loading images from flickr.\n" + count.toString() + "\t/ " + n_planes.toString();
// 画像が全部ロードし終わったらレンダリング開始
if(count == n_planes)
{
label.visible = false;
addEventListener(Event.ENTER_FRAME, onFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
// 計算更新用関数
private function onFrame(e:Event):void
{
// カメラの移動
if (isMouseDown)
{
targetRot+=(mouseX - oldX) * 0.2;
targetPitch+=(mouseY - oldY) * 0.2;
targetPitch=(targetPitch > -90) ? (targetPitch) : (-90);
targetPitch=(targetPitch < 90) ? (targetPitch) : (90);
oldX=mouseX;
oldY=mouseY;
}
rot+=(targetRot - rot) * 0.1;
pitch+=(targetPitch - pitch) * 0.1;
pitch=(pitch > -90) ? (pitch) : (-90);
pitch=(pitch < 90) ? (pitch) : (90);
camera.x=CAMERA_RADIUS * Math.sin(rot * Math.PI / 180);
camera.y=CAMERA_RADIUS * Math.sin(pitch * Math.PI / 180);
camera.z=CAMERA_RADIUS * Math.cos(rot * Math.PI / 180);
// ビューイング変換行列
var Mview:MyMatrix3D=getViewingTransformMatrix(camera);
// レンダリング
for(var i:int = 0 ; i<n_planes ; i++) (container[i] as MyPlane).render(Mview);
// ソート
container.sortOn("sortNumber", Array.NUMERIC | Array.DESCENDING);
// 描写
graphics.clear();
for(i=0 ; i<n_planes ; i++)
{
var plane:MyPlane = container[i] as MyPlane;
plane.draw(graphics, CENTER_X, CENTER_Y, FOCUS, img_array[plane.imgNumber] as BitmapData);
}
}
// マウスダウン時の関数
private function onMouseDown(e:MouseEvent):void
{
isMouseDown=true;
oldX=mouseX;
oldY=mouseY;
}
// マウスアップ時の関数
private function onMouseUp(e:MouseEvent):void
{
isMouseDown=false;
}
// カメラ位置からビューイング変換行列を返す関数
private function getViewingTransformMatrix(camera:MyVector3D):MyMatrix3D
{
// Center - Eye ベクトルの作成と正規化
var n:MyVector3D=CENTER.subtract(camera);
n.normalize();
// up×nを計算 (外積)
var u:MyVector3D=UP.crossProduct(n);
u.normalize();
// n×uを計算 (外積)
var v:MyVector3D=n.crossProduct(u);
v.normalize();
// dの計算
var dx:Number=-camera.innerProduct(u);
var dy:Number=-camera.innerProduct(v);
var dz:Number=-camera.innerProduct(n);
// ビューイング変換行列
var Mview:MyMatrix3D=new MyMatrix3D(u.x, u.y, u.z, dx, v.x, v.y, v.z, dy, n.x, n.y, n.z, dz, 0, 0, 0, 1);
return Mview;
}
}
}
/*
Planeクラス
*/
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.geom.Matrix;
import flash.geom.Point;
final class MyPlane
{
// 上を示すベクトル
private const CENTER:MyVector3D = new MyVector3D();
// モデルビュー変換後の座標を保存する配列
private var eye_Coord_points:Array;
// パラメータ
public var x:Number;
public var y:Number;
public var z:Number;
public var rotationX:Number;
public var rotationY:Number;
public var rotationZ:Number;
public var size:Number
// ソート用変数・画像用ナンバー
public var sortNumber:Number;
public var imgNumber:int;
// コンストラクタ
public function MyPlane(size:Number, imgNumber:int)
{
x = y = z = rotationX = rotationY = rotationZ = 0;
eye_Coord_points=[];
this.size = size;
this.imgNumber = imgNumber;
}
// 座標の更新
public function render(Mview:MyMatrix3D):void
{
// 四隅の座標を作成
var s:Number = size >> 1;
var original_points:Array=[new MyVector3D(-s, s), new MyVector3D(s, s), new MyVector3D(-s, -s), new MyVector3D(s, -s)];
// モデリング変換用行列
var Mmodel:MyMatrix3D=new MyMatrix3D;
Mmodel=Mmodel.productParallelTransformMatrix(x, y, z);
Mmodel=Mmodel.productRotationZMatrix(rotationZ);
Mmodel=Mmodel.productRotationYMatrix(rotationY);
Mmodel=Mmodel.productRotationXMatrix(rotationX);
// モデルビュー変換行列
var Mmodelview:MyMatrix3D=Mview.productMatrix(Mmodel);
// 座標系とソート用変数を計算
sortNumber=0;
eye_Coord_points=[];
for(var i:int=0; i < 4; i++)
{
var p:MyVector3D=Mmodelview.productVector(original_points[i]as MyVector3D);
eye_Coord_points[i]=p;
sortNumber+=p.z;
}
}
// 描写
public function draw(graphics:Graphics, centerX:Number, centerY:Number, focus:Number, bmpd:BitmapData):void
{
// 3D→2Dの変換
var points:Array=[];
for(var i:int=0; i < 4; i++)
{
var p:MyVector3D=eye_Coord_points[i]as MyVector3D;
var scale:Number=focus / CENTER.distance(p);
var screenX:Number=p.x * scale + centerX;
var screenY:Number=-p.y * scale + centerY;
points[i]=new Point(screenX, screenY);
}
// 描写
// こちらを参考にしました。:http://www.d-project.com/flex/009_FreeTransform/
var a0:Point=new Point();
var a1:Point=new Point(bmpd.width, 0);
var a2:Point=new Point(0, bmpd.height);
var a3:Point=new Point(bmpd.width, bmpd.height);
var b0:Point=points[0]as Point;
var b1:Point=points[1]as Point;
var b2:Point=points[2]as Point;
var b3:Point=points[3]as Point;
var mtx:Matrix = createMatrix(a0,a1,a2,b0,b1,b2);
graphics.beginBitmapFill(bmpd, mtx);
graphics.moveTo(b0.x, b0.y);
graphics.lineTo(b1.x, b1.y);
graphics.lineTo(b2.x, b2.y);
graphics.endFill();
mtx = createMatrix(a3,a1,a2,b3,b1,b2);
graphics.beginBitmapFill(bmpd, mtx);
graphics.moveTo(b3.x, b3.y);
graphics.lineTo(b1.x, b1.y);
graphics.lineTo(b2.x, b2.y);
graphics.endFill();
}
// 画像変形用行列の作成用関数
// こちらのものを使わせてもらいました。:http://www.d-project.com/flex/009_FreeTransform/
private function createMatrix(a0:Point, a1:Point, a2:Point, point0:Point, point1:Point, point2:Point):Matrix
{
var ma:Matrix=new Matrix(a1.x - a0.x, a1.y - a0.y, a2.x - a0.x, a2.y - a0.y);
var mb:Matrix=new Matrix(point1.x - point0.x, point1.y - point0.y, point2.x - point0.x, point2.y - point0.y);
ma.invert();
var m:Matrix=new Matrix();
m.translate(-a0.x, -a0.y);
m.concat(ma);
m.concat(mb);
m.translate(point0.x, point0.y);
return m;
}
}
/*
行列演算用クラス
*/
final class MyMatrix3D
{
// 行列の要素
public var v11:Number, v12:Number, v13:Number, v14:Number;
public var v21:Number, v22:Number, v23:Number, v24:Number;
public var v31:Number, v32:Number, v33:Number, v34:Number;
public var v41:Number, v42:Number, v43:Number, v44:Number;
// 計算用ラジアン
private static const RADIAN:Number=Math.PI / 180;
// コンストラクタ
public function MyMatrix3D(v11:Number=1, v12:Number=0, v13:Number=0, v14:Number=0,
v21:Number=0, v22:Number=1, v23:Number=0, v24:Number=0,
v31:Number=0, v32:Number=0, v33:Number=1, v34:Number=0,
v41:Number=0, v42:Number=0, v43:Number=0, v44:Number=1)
{
this.v11=v11; this.v12=v12; this.v13=v13; this.v14=v14;
this.v21=v21; this.v22=v22; this.v23=v23; this.v24=v24;
this.v31=v31; this.v32=v32; this.v33=v33; this.v34=v34;
this.v41=v41; this.v42=v42; this.v43=v43; this.v44=v44;
}
// 行列との積 (自分をA、引数をBとするとA*B)
public function productMatrix(m:MyMatrix3D):MyMatrix3D
{
var newMat:MyMatrix3D=new MyMatrix3D();
newMat.v11=v11 * m.v11 + v12 * m.v21 + v13 * m.v31 + v14 * m.v41;
newMat.v12=v11 * m.v12 + v12 * m.v22 + v13 * m.v32 + v14 * m.v42;
newMat.v13=v11 * m.v13 + v12 * m.v23 + v13 * m.v33 + v14 * m.v43;
newMat.v14=v11 * m.v14 + v12 * m.v24 + v13 * m.v34 + v14 * m.v44;
newMat.v21=v21 * m.v11 + v22 * m.v21 + v23 * m.v31 + v24 * m.v41;
newMat.v22=v21 * m.v12 + v22 * m.v22 + v23 * m.v32 + v24 * m.v42;
newMat.v23=v21 * m.v13 + v22 * m.v23 + v23 * m.v33 + v24 * m.v43;
newMat.v24=v21 * m.v14 + v22 * m.v24 + v23 * m.v34 + v24 * m.v44;
newMat.v31=v31 * m.v11 + v32 * m.v21 + v33 * m.v31 + v34 * m.v41;
newMat.v32=v31 * m.v12 + v32 * m.v22 + v33 * m.v32 + v34 * m.v42;
newMat.v33=v31 * m.v13 + v32 * m.v23 + v33 * m.v33 + v34 * m.v43;
newMat.v34=v31 * m.v14 + v32 * m.v24 + v33 * m.v34 + v34 * m.v44;
newMat.v41=v41 * m.v11 + v42 * m.v21 + v43 * m.v31 + v44 * m.v41;
newMat.v42=v41 * m.v12 + v42 * m.v22 + v43 * m.v32 + v44 * m.v42;
newMat.v43=v41 * m.v13 + v42 * m.v23 + v43 * m.v33 + v44 * m.v43;
newMat.v44=v41 * m.v14 + v42 * m.v24 + v43 * m.v34 + v44 * m.v44;
return newMat;
}
// ベクトルとの積
public function productVector(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=v11 * v.x + v12 * v.y + v13 * v.z + v14;
newVec.y=v21 * v.x + v22 * v.y + v23 * v.z + v24;
newVec.z=v31 * v.x + v32 * v.y + v33 * v.z + v34;
return newVec;
}
// x軸回転行列との積を返す
public function productRotationXMatrix(rotationX:Number):MyMatrix3D
{
var sin:Number=Math.sin(rotationX * RADIAN);
var cos:Number=Math.cos(rotationX * RADIAN);
var mat:MyMatrix3D=new MyMatrix3D(1, 0, 0, 0, 0, cos, -sin, 0, 0, sin, cos);
return productMatrix(mat);
}
// y軸回転行列との積を返す
public function productRotationYMatrix(rotationY:Number):MyMatrix3D
{
var sin:Number=Math.sin(rotationY * RADIAN);
var cos:Number=Math.cos(rotationY * RADIAN);
var mat:MyMatrix3D=new MyMatrix3D(cos, 0, sin, 0, 0, 1, 0, 0, -sin, 0, cos);
return productMatrix(mat);
}
// z軸回転行列との積を返す
public function productRotationZMatrix(rotationZ:Number):MyMatrix3D
{
var sin:Number=Math.sin(rotationZ * RADIAN);
var cos:Number=Math.cos(rotationZ * RADIAN);
var mat:MyMatrix3D=new MyMatrix3D(cos, -sin, 0, 0, sin, cos);
return productMatrix(mat);
}
// 平行移動の行列との積を返す
public function productParallelTransformMatrix(x:Number, y:Number, z:Number):MyMatrix3D
{
var mat:MyMatrix3D=new MyMatrix3D(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z);
return productMatrix(mat);
}
}
/*
ベクトル演算用クラス
*/
final class MyVector3D
{
// x、y、z座標を保存する変数
public var x:Number;
public var y:Number;
public var z:Number;
public var w:Number;
// コンストラクタ
public function MyVector3D(x:Number=0, y:Number=0, z:Number=0)
{
this.x=x;
this.y=y;
this.z=z;
this.w=1;
}
// 距離
public function distance(v:MyVector3D):Number
{
return Math.sqrt((v.x - x) * (v.x - x) + (v.y - y) * (v.y - y) + (v.z - z) * (v.z - z));
}
// 差
public function subtract(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=x - v.x;
newVec.y=y - v.y;
newVec.z=z - v.z;
return newVec;
}
// 正規化
public function normalize():void
{
var len:Number=Math.sqrt(x * x + y * y + z * z);
x/=len;
y/=len;
z/=len;
}
// 内積
public function innerProduct(v:MyVector3D):Number
{
return (x * v.x + y * v.y + z * v.z);
}
// 外積(クロス積) (自分をA、引数をBとすると、A×Bを計算)
public function crossProduct(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=y * v.z - z * v.y;
newVec.y=z * v.x - x * v.z;
newVec.z=x * v.y - y * v.x;
return newVec;
}
}