simple 3D 04 (not using PV3D)
PV3Dとかを使わない3D表現その4
前のをちょいいじっただけ。
Vector3D & Matrix3D は自作。
/**
* 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;
}
}