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 04 (not using PV3D)

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

前のをちょいいじっただけ。

Vector3D & Matrix3D は自作。
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/plbN
 */

/*
    PV3Dとかを使わない3D表現その4

    前のをちょいいじっただけ。

    Vector3D & Matrix3D は自作。
*/
package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Loader;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import net.hires.debug.Stats;
    import net.wonderfl.utils.SequentialLoader;
    
    [SWF(width="465", height="465", backgroundColor="0xffffff")]
    public class simple3d_04 extends Sprite
    {
        // 画像のURL
        private const imgURL:String="http://assets.wonderfl.net/images/related_images/e/e5/e571/e571da8723af54fb25a19d90523b2729f7208d08";
        
        // 3Dボール用
        private static const RADIUS:Number=180;
        private static const N_POINT1:int=10;
        private static const N_POINT2:int=10;
        private static const RADIAN:Number=Math.PI / 180;
        
        // 被写界深度画像用
        private static const N_BLUR_IMG:int=20;
        private static const BLUR_RANGE:int=180;
        private static const BLUR_NUMBER:Number = 0.7;
        private static const STEP:Number=BLUR_RANGE / N_BLUR_IMG;
        private static const N_COLOR:int=4;
        private static const BALL_SIZE:int=50;
        
        // 幅・高さ・中心
        private static const CENTER_X:int=465 >> 1;
        private static const CENTER_Y:int=465 >> 1;
        private static const W:int=465;
        private static const H:int=465;
        
        // 上を示すベクトル・中心を示すベクトル・フォーカス 
        private var UP_VECTOR:MyVector3D=new MyVector3D(0, 1, 0);
        private var CENTER_VECTOR:MyVector3D=new MyVector3D(0, 0, 0);
        private var FOCUS:Number=360;
        
        // 3Dボールを保持する配列・画像を保持する配列・3DBallの数を保持する変数
        private var ballAry:Array;
        private var imgAry:Array;
        private var n_balls:int;
        
        // 画像ロード用
        private var imgs:Array;
        
        // カメラ用
        private var camera:MyVector3D;
        private var camera_radius:int;
        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;
        
        // コンストラクタ
        public function simple3d_04()
        {
            imgs=[];
            SequentialLoader.loadImages([imgURL], imgs, onLoaded);

        }
        
        
        // 初期化用関数
        public function onLoaded():void
        {
            // 画像の読み込み
            var ldr:Loader=imgs.pop();
            var ballImg:BitmapData=(ldr.content as Bitmap).bitmapData;
            
            // ステージ設定、マウス関係停止。
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.quality=StageQuality.HIGH;
            stage.frameRate = 60;
            mouseChildren=false;
            mouseEnabled=false;
            
            // Stats追加
            addChild(new Stats());
            
            // 変数の初期化
            oldX = oldY = rot =pitch= n_balls = 0;
            targetPitch  = 90;
            targetRot = 180;
            isMouseDown = false;
            camera_radius = 360;
            camera=new MyVector3D(0, 0, FOCUS);
            
            // 被写界深度用の画像を作成
            createBlurImages(ballImg);
            
            // ボールの初期位置を計算
            initBallPosition();
            
            // 更新用関数追加
            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();
            
            // モデリング変換変換行列 (今回は何もしないのでただの単位行列)
            var Mmodel:MyMatrix3D=new MyMatrix3D;
            
            // 各ボールに座標変換を適用
            for(var i:int=0; i < n_balls; i++)  calcCoordinate(ballAry[i]as MyBall3D, Mmodel, Mview);
            
            // zソート
            ballAry.sortOn("sortNumber", Array.NUMERIC | Array.DESCENDING);
            for(i=0; i < n_balls; i++) setChildIndex(ballAry[i]as MyBall3D, i)
        }
        
        // 被写界深度の画像を作成
        private function createBlurImages(ballImg:BitmapData):void
        {
            imgAry=[];
            var p:Point=new Point;
            
            for(var i:int=0; i < N_COLOR; i++)
            {
                var tmp1:BitmapData=new BitmapData(BALL_SIZE, BALL_SIZE, true, 0xffffff);
                tmp1.copyPixels(ballImg, new Rectangle(BALL_SIZE * i, 0, BALL_SIZE, BALL_SIZE), p);
                imgAry[i]=[];
                
                for(var j:int=0; j < N_BLUR_IMG; j++)
                {
                    var tmp2:BitmapData=tmp1.clone();
                    tmp2.applyFilter(tmp2, tmp2.rect, p, new BlurFilter(BLUR_NUMBER * j, BLUR_NUMBER * j));
                    imgAry[i][j]=tmp2;
                }
            }
            
        }
        
        // 各ボールの初期位置計算用関数
        private function initBallPosition():void
        {
            ballAry=[];
            
            for(var i:int=0; i < N_POINT1; i++)
            {
                var theta1:Number=(360 / N_POINT1) * i * RADIAN;
                var start:int=(i == 0) ? (0) : (1);
                for(var j:int=start; j < N_POINT2; j++)
                {
                    var theta2:Number=((180 / N_POINT2) * j - 90) * RADIAN;
                    var xx:Number=RADIUS * Math.cos(theta2) * Math.sin(theta1);
                    var yy:Number=RADIUS * Math.sin(theta2);
                    var zz:Number=RADIUS * Math.cos(theta2) * Math.cos(theta1);
                    var ball:MyBall3D=new MyBall3D(new BitmapData(BALL_SIZE, BALL_SIZE, false), xx, yy, zz, n_balls % N_COLOR);
                    ballAry[n_balls]=ball;
                    addChild(ball);
                    n_balls++;
                }
            }
        }
        
        // マウスダウン時の関数
        private function onMouseDown(e:MouseEvent):void
        {
            isMouseDown=true;
            oldX=mouseX;
            oldY=mouseY;
        }
        
        // マウスアップ時の関数
        private function onMouseUp(e:MouseEvent):void
        {
            isMouseDown=false;
        }
        
        // カメラ位置からビューイング変換行列を返す関数
        private function getViewingTransformMatrix():MyMatrix3D
        {
            // Center - Eye ベクトルの作成と正規化
            var n:MyVector3D=CENTER_VECTOR.subtract(camera);
            n.normalize();
            
            // up×nを計算 (外積)
            var u:MyVector3D=UP_VECTOR.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;
        }
        
        // 座標変換と被写界深度を計算する関数
        private function calcCoordinate(ball:MyBall3D, Mmodel:MyMatrix3D, Mview:MyMatrix3D):void
        {
            var position:MyVector3D=ball.position;
            
            // 座標変換
            position=Mmodel.productVector(position);
            var eyeCord_position:MyVector3D=Mview.productVector(position);
            
            // カメラとの距離計算
            ball.sortNumber=eyeCord_position.distance(CENTER_VECTOR);
            
            // 3D→2D変換 
            var scale:Number =FOCUS / ball.sortNumber;
            var screenX:Number=((eyeCord_position.x)) * scale + CENTER_X;
            var screenY:Number=-((eyeCord_position.y)) * scale + CENTER_Y;
            
            // 計算反映
            ball.scaleX=ball.scaleY=scale;
            ball.x=screenX;
            ball.y=screenY;
            
            // BitmapDataの更新
            var blurIndex:int=((FOCUS - ball.sortNumber) / STEP) >> 0;
            blurIndex=(blurIndex ^ (blurIndex >> 31)) - (blurIndex >> 31)
            blurIndex=(blurIndex >= N_BLUR_IMG) ? (N_BLUR_IMG - 1) : (blurIndex);
            ball.bitmapData=imgAry[ball.ballColor][blurIndex]as BitmapData;
            
            // 座標の保存
            ball.position=position;
        }
    }
}


/*
    3Dボールクラス
*/
import flash.display.Bitmap;
import flash.display.BitmapData;

final class MyBall3D extends Bitmap
{
    public var position:MyVector3D;
    public var ballColor:int;
    public var sortNumber:Number;
    private var bmpd_w:Number;
    private var bmpd_h:Number;
    
    public function MyBall3D(material:BitmapData, x:Number=0, y:Number=0, z:Number=0, ballColor:int=0)
    {
        position=new MyVector3D(x, y, z);
        sortNumber=0;
        this.ballColor = ballColor;
        bitmapData=material;
        bmpd_w=material.width >> 1;
        bmpd_h=material.height >> 1;
    }
    
    override public function set x(value:Number):void
    {
        super.x=value - bmpd_w;
    }
    
    override public function set y(value:Number):void
    {
        super.y=value - bmpd_h;
    }
}



/*
    行列演算用クラス
*/
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;
    
    // コンストラクタ
    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;
    }
}


/*
    ベクトル演算用クラス
*/
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;
    }
}