simple 3D 03 (not using PV3D)
PV3Dとかを使わない3D表現その3
カメラの移動を追加。
座標計算は行列&ベクトルに変更。
Matrix3DとVector3Dは自作のを。
ボールはx軸回転。
カメラはマウスの位置によってY軸周りを動きます。
カメラが動いてるってのが分かりにくいかも。
カメラの動きはこちらのものを拝借させてもらってます。
http://wonderfl.net/c/lBrO
/**
* Copyright sakef ( http://wonderfl.net/user/sakef )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hMDD
*/
/*
PV3Dとかを使わない3D表現その3
カメラの移動を追加。
座標計算は行列&ベクトルに変更。
Matrix3DとVector3Dは自作のを。
ボールはx軸回転。
カメラはマウスの位置によってY軸周りを動きます。
カメラが動いてるってのが分かりにくいかも。
カメラの動きはこちらのものを拝借させてもらってます。
http://wonderfl.net/c/lBrO
*/
package
{
import net.wonderfl.utils.SequentialLoader;
import net.hires.debug.Stats;
import flash.display.Loader;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.Point;
[SWF(width="465", height="465", backgroundColor="0xffffff", frameRate="30")]
public class simple3d_03 extends Sprite
{
// 画像のURL
private const imgURL:String="http://assets.wonderfl.net/images/related_images/6/64/643a/643a412effc1d96e4df242ad7590fe90fa7a56c1";
// 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 SIN2:Number=Math.sin(RADIAN * 2);
private static const COS2:Number=Math.cos(RADIAN * 2);
// 被写界深度画像用
private static const N_BLUR_IMG:int=20;
private static const BLUR_RANGE:int=140;
private static const STEP:Number=BLUR_RANGE / N_BLUR_IMG;
// 幅・高さ・中心
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 camera:MyVector3D;
private var camera_radius:int;
private var theta:Number;
private var camera_calc_flug:int;
// 画像ロード用
private var imgs:Array;
// コンストラクタ
public function simple3d_03()
{
imgs=[];
SequentialLoader.loadImages([imgURL], imgs, onLoaded);
}
// 初期化用関数
public function onLoaded():void
{
// 画像の読み込み
var ldr:Loader=imgs.pop();
var ballImg:BitmapData=(ldr.content as Bitmap).bitmapData;
// ステージ設定、マウス関係停止。
stage.quality=StageQuality.LOW;
mouseChildren=false;
mouseEnabled=false;
// Stats追加
addChild(new Stats());
// 変数の初期化
var i:int, j:int;
theta=n_balls=0;
camera_calc_flug=1;
camera_radius=360;
ballAry=[];
imgAry=[];
camera=new MyVector3D(0, 0, FOCUS);
// ブラー付きのBitmapDataを先に作っておくべ。
var p:Point=new Point();
for(i=0; i < N_BLUR_IMG; i++)
{
var tmp:BitmapData=ballImg.clone();
tmp.applyFilter(tmp, tmp.rect, p, new BlurFilter(0.6 * i, 0.6 * i));
imgAry[i]=tmp;
}
// 各点の初期位置を計算
for(i=0; i < N_POINT1; i++)
{
var theta1:Number=(360 / N_POINT1) * i * RADIAN;
var start:int=(i == 0) ? (0) : (1);
for(j=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(ballImg, xx, yy, zz);
ballAry[n_balls]=ball;
addChild(ball);
n_balls++;
}
}
// 更新用関数追加
addEventListener(Event.ENTER_FRAME, onFrame);
}
// 計算更新用関数
private function onFrame(e:Event):void
{
// カメラの移動
theta+=10 * (mouseX / stage.stageWidth - 0.5);
camera.x=camera_radius * Math.sin(RADIAN * theta);
camera.z=camera_radius * Math.cos(RADIAN * theta);
// 半径計算 (大きくなったり小さくなったり)
if (camera_calc_flug == 1)
{
camera_radius+=2;
if (camera_radius > 600) camera_calc_flug=0;
}
else
{
camera_radius-=2;
if (camera_radius < 250) camera_calc_flug=1;
}
// 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);
// モデリング変換変換行列 (X軸回転)
var Mmodel:MyMatrix3D=new MyMatrix3D(1, 0, 0, 0, 0, COS2, -SIN2, 0, 0, SIN2, COS2, 0, 0, 0, 0, 1);
// モデルビュー変換行列
var Mmodeview:MyMatrix3D=Mview.productMatrix(Mmodel);
// 各ボールに座標変換を適用
var ball:MyBall3D;
var position:MyVector3D;
var eyeCord_position:MyVector3D;
var scale:Number;
var screenX:int, screenY:int;
var blurIndex:int;
for(var i:int=0; i < n_balls; i++)
{
ball=ballAry[i]as MyBall3D;
position=ball.position;
// 座標変換
position=Mmodel.productVector(position);
eyeCord_position=Mmodeview.productVector(position);
// カメラとの距離計算
ball.sortNumber=eyeCord_position.distance(CENTER_VECTOR);
// 3D→2D変換
scale=FOCUS / ball.sortNumber;
screenX=((eyeCord_position.x)) * scale + CENTER_X;
screenY=-((eyeCord_position.y)) * scale + CENTER_Y;
// 計算反映
ball.scaleX=ball.scaleY=scale;
ball.x=screenX;
ball.y=screenY;
// BitmapDataの更新
blurIndex=((FOCUS - ball.sortNumber) / STEP) >> 0;
blurIndex=(blurIndex ^ (blurIndex >> 31)) - (blurIndex >> 31)
blurIndex=(blurIndex >= N_BLUR_IMG) ? (N_BLUR_IMG - 1) : (blurIndex);
ball.bitmapData=imgAry[blurIndex]as BitmapData;
// 座標の保存
ball.position=position;
}
// zソート
ballAry.sortOn("sortNumber", Array.NUMERIC | Array.DESCENDING);
for(i=0; i < n_balls; i++)
{
ball=ballAry[i]as MyBall3D;
setChildIndex(ball, i)
}
}
}
}
/*
3Dボールクラス
*/
import flash.display.Bitmap;
import flash.display.BitmapData;
final class MyBall3D extends Bitmap
{
public var position:MyVector3D;
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)
{
position=new MyVector3D(x, y, z);
sortNumber=0;
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;
}
}