Matrix3D.transformVectors による 3D 表現の雛形
MaMatrix3D.transformVectors による 3D 表現の雛形
@author YOSHIDA, Akio (Aquioux)
解説ページ http://aquioux.blog48.fc2.com/blog-entry-648.html
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/cA3I
*/
// forked from Aquioux's Utils3D.projectVectors による 3D 表現の雛形
package {
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
/**
* MaMatrix3D.transformVectors による 3D 表現の雛形
* @author YOSHIDA, Akio (Aquioux)
* 解説ページ http://aquioux.blog48.fc2.com/blog-entry-648.html
*/
public class Main extends Sprite {
public function Main() {
// 座標データを生成
var data:DataFactory1 = new DataFactory1(200.0); // 環
//var data:DataFactory2 = new DataFactory2(100.0); // 立方体
data.addEventListener(Event.COMPLETE, completeHandler);
data.start();
}
private function completeHandler(event:Event):void {
// Model を生成
var model:Model = new Model(event.target.verts.concat());
model.offsetX = stage.stageWidth / 2;
model.offsetY = stage.stageHeight / 2;
model.offsetZ = -100;
model.speed = 0.01;
//model.fieldOfView = 55;
model.fieldOfView = 75;
// View を生成
var view:View = new View(model);
addChild(view);
// Controller を生成
var controller:Controller = new Controller(model);
controller.setup(stage);
}
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
class DataFactory1 extends EventDispatcher {
// 外部に渡すデータ
public function get verts():Vector.<Number> { return _verts; }
private var _verts:Vector.<Number> = new Vector.<Number>();
private var length:Number;
public function DataFactory1(length:Number) {
this.length = length;
}
internal function start():void{
var NUM_OF_DOT:uint = 8;
var baseRadian:Number = Math.PI * 2 / NUM_OF_DOT;
for (var i:int = 0; i < NUM_OF_DOT; i++) {
var point:Point = Point.polar(length, baseRadian * i);
_verts.push(point.x, point.y, 0);
}
_verts.fixed = true;
dispatchEvent(new Event(Event.COMPLETE));
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
class DataFactory2 extends EventDispatcher {
// 外部に渡すデータ
public function get verts():Vector.<Number> { return _verts; }
private var _verts:Vector.<Number> = new Vector.<Number>();
private var length:Number;
public function DataFactory2(length:Number) {
this.length = length;
}
internal function start():void {
_verts.push( length, length, length);
_verts.push( length, -length, length);
_verts.push(-length, -length, length);
_verts.push(-length, length, length);
_verts.push( length, length, -length);
_verts.push( length, -length, -length);
_verts.push(-length, -length, -length);
_verts.push(-length, length, -length);
_verts.fixed = true;
dispatchEvent(new Event(Event.COMPLETE));
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Vector3D;
import flash.geom.Utils3D;
class Model extends EventDispatcher {
// 外部へ渡すデータ
public function get data():Vector.<Number> { return _data; }
private var _data:Vector.<Number>;
// 外部から受けるデータ
// 引数として受ける
private var vin:Vector.<Number>;
// セッターで受ける
// X座標オフセット
public function set offsetX(value:Number):void {
_offsetX = value;
}
private var _offsetX:Number = 0;
// Y座標オフセット
public function set offsetY(value:Number):void {
_offsetY = value;
}
private var _offsetY:Number = 0;
// Z座標オフセット
public function set offsetZ(value:Number):void {
_offsetZ = value;
}
private var _offsetZ:Number = 500;
// マウス位置を Matrix3D の回転に変換するときに使用する変数
public function set speed(value:Number):void {
_speed = value;
}
private var _speed:Number = 0.025;
// 内部パースペクティブプロジェクションの fieldOfView
public function set fieldOfView(value:Number):void {
projection.fieldOfView = value;
}
private var _fieldOfView:Number;
private const NUM_OF_BLOCK:uint = 3;
private var vx:Number = 0;
private var vy:Number = 0;
// Controller から呼び出される
internal function update(valueX:Number, valueY:Number):void {
// Controller から渡された値を加工
vx += (_offsetX - valueX) * _speed;
vy -= (_offsetY - valueY) * _speed;
// Controller から渡された値を回転に変換
m.identity();
m.appendRotation(vy, Vector3D.X_AXIS);
m.appendRotation(vx, Vector3D.Y_AXIS);
m.appendTranslation(0, 0, _offsetZ);
// 外部データに回転を適用
m.transformVectors(vin, vout);
// 回転を適用した外部データを zsort
var n:uint = vout.length / NUM_OF_BLOCK;
for (var i:int = 0; i < n; i++) {
var vector3D:Vector3D = new Vector3D();
vector3D.x = vout[i * NUM_OF_BLOCK];
vector3D.y = vout[i * NUM_OF_BLOCK + 1];
vector3D.z = vout[i * NUM_OF_BLOCK + 2];
vector3D.w = projection.focalLength / (projection.focalLength + vector3D.z);
vector3D.project();
sorted[i] = vector3D;
}
sorted.sortOn("z", Array.NUMERIC);
// sort した Array から必要なデータを抽出・生成し、Vector.<Number> に代入
for (i = 0; i < n; i++) {
vector3D = sorted[i];
_data[i * NUM_OF_BLOCK] = vector3D.x + _offsetX;
_data[i * NUM_OF_BLOCK + 1] = vector3D.y + _offsetY;
_data[i * NUM_OF_BLOCK + 2] = 1 / vector3D.w;
}
// リスナー(View)へ通知
notifyListeners();
}
private var vout:Vector.<Number>;
private var sorted:Array;
private var projection:PerspectiveProjection;
private var m:Matrix3D;
public function Model(vin:Vector.<Number>) {
// 外部から受けるデータを引数として受ける
this.vin = vin;
var n:uint = vin.length;
// 外部に渡すデータの初期化
_data = new Vector.<Number>(n, true);
// 内部でのみ使用する Vector の初期化
vout = new Vector.<Number>(n, true);
// 内部でのみ使用する Array の初期化
sorted = [];
// 内部でのみ使用する PerspectiveProjection の生成
projection = new PerspectiveProjection();
// 内部でのみ使用する外部データに適用する Matrix3D
m = new Matrix3D();
}
// View へ伝える
private function notifyListeners():void {
dispatchEvent(new Event(Event.CHANGE));
}
}
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Vector3D;
class View extends Sprite {
private function changeHandler(event:Event):void {
// Model からデータを受ける
var data:Vector.<Number> = model.data;
// 描画
canvas.graphics.clear();
const NUM_OF_BLOCK:uint = 3;
var n:uint = data.length / NUM_OF_BLOCK;
for (var i:int = 0; i < n; i++) {
var posX:Number = data[i * NUM_OF_BLOCK];
var posY:Number = data[i * NUM_OF_BLOCK + 1];
var scale:Number = data[i * NUM_OF_BLOCK + 2];
var c:uint = 0xFF * scale;
c = c > 0xFF ? 0xFF : c;
canvas.graphics.beginFill(c << 16 | c << 8 | c);
canvas.graphics.drawCircle(posX, posY, 20 * scale);
canvas.graphics.endFill();
}
}
private var model:Model;
public function View(model:Model) {
this.model = model;
this.model.addEventListener(Event.CHANGE, changeHandler);
addEventListener(Event.ADDED_TO_STAGE, setup);
}
private var canvas:Sprite;
private function setup(event:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, setup);
canvas = new Sprite();
addChild(canvas);
}
}
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
/**
* ENTER_FRAME のタイミングでマウスの位置を Model に渡す
* @author YOSHIDA, Akio (Aquioux)
*/
class Controller {
private var stage:Stage;
internal function setup(stage:Stage):void {
this.stage = stage;
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(event:Event):void {
model.update(stage.mouseX, stage.mouseY);
}
private var model:Model;
public function Controller(model:Model) {
this.model = model;
}
}