In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

simple 3D 06 (not using PV3D)

PV3Dとかを使わない3D表現その6

画像をFlickrから取ってきて表示しました。
画像をゆがめる方法は以下を参考にしました。
http://www.d-project.com/flex/009_FreeTransform/

Matrix3DとVector3Dは自作。Matrix3Dはちょっと改造しました。

やればやるほどPV3Dなど3Dライブラリの凄さを実感します。
Get Adobe Flash player
by sakef 28 Oct 2010
/**
 * 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;
    }
}