// forked from yeq's Astro Cube /* * Astro Cube * * I've refered to the following posts * てっく煮ブログ * * AS3.0 で 3D プログラミングを1から勉強する (1)~(4) * http://d.hatena.ne.jp/nitoyon/20080616/as_3d_lessen1 * http://d.hatena.ne.jp/nitoyon/20080617/as_3d_lessen2 * http://d.hatena.ne.jp/nitoyon/20080618/as_3d_lessen3 * http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3 * * Adobe デベロッパーセンター * * Matrixクラス - 変換行列 * http://www.adobe.com/jp/devnet/flash/articles/matrix_class.html * * Matrix3Dクラス - 変換行列2 * http://www.adobe.com/jp/devnet/flash/articles/matrix3d_class.html * * fumioNonaka.com * * Adobe Flash非公式テクニカルノート - 変換行列を数学的に捉える * http://www.fumiononaka.com/TechNotes/Flash/FN0811001.html * * */ package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.geom.Vector3D; import flash.geom.Matrix3D; import flash.display.Graphics; import __AS3__.vec.Vector; import flash.geom.PerspectiveProjection; import flash.utils.Dictionary; [SWF(backgroundColor="#FFFFFF", frameRate="30")] public class Astro extends Sprite { public static var fov:PerspectiveProjection= new PerspectiveProjection(); private var canvas:Sprite; private var rad:int; private var cube:Cube; public function Astro() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; cube = new Cube(0, 0, 0, 200) canvas = new Sprite(); addChild(canvas); canvas.x = 200; canvas.y = 200; canvas.z = 0; //視野角の設定 fov.fieldOfView = 45; rad = 0; addEventListener(Event.ENTER_FRAME, function(evt:Event):void{render();}); } private function render():void { canvas.graphics.clear(); // 回転行列を作成 var matrix:Matrix3D= new Matrix3D(); //X軸方向に傾ける matrix.appendRotation(Math.PI/180*rad/2, Vector3D.X_AXIS); //Y軸方向に傾ける matrix.appendRotation(Math.PI/180*rad*3, Vector3D.Y_AXIS); //Z軸方向に傾ける matrix.appendRotation(Math.PI/180*rad, Vector3D.Z_AXIS); cube.draw(canvas.graphics, matrix); // 角度更新 rad += 10; } } } import flash.geom.Vector3D; import flash.geom.Matrix3D; import flash.display.Graphics; import __AS3__.vec.Vector; import flash.geom.PerspectiveProjection; import flash.utils.Dictionary; class Cube { private var points:Vector.<Vector3D> = new Vector.<Vector3D>(); public function Cube(x:Number, y:Number, z:Number, len:Number) { var diff:Function = function(f:Boolean):Number{return f ? len / 2 : -len / 2;}; // 立方体の頂点8つを作成する for(var i:int = 0; i < 8; i++) { var p:Vector3D= new Vector3D(x + diff(i % 4 % 3 == 0), y + diff(i % 4 < 2), z + diff(i < 4)); points.push(p); } } public function draw(g:Graphics, matrix:Matrix3D):void { // 回転後の各頂点の座標を計算 var p:Vector.<Vector3D> = new Vector.<Vector3D>(); for(var i:int = 0; i < points.length; i++) { //matrix.appendTranslation(points[i].x, points[i].y, points[i].z); var pt:Vector3D= matrix.transformVector(points[i]); var fov:PerspectiveProjection = Astro.fov; // 点を透視投影する pt.w = fov.focalLength / (fov.focalLength + pt.z); pt.project(); //drawPoint(g, pt); p.push(pt); } // 頂点の間を線で結ぶ for(i = 0; i < 4; i++) { //drawLine(g, p[i], p[i + 4]); //drawLine(g, p[i], p[(i + 1) % 4]); //drawLine(g, p[i + 4], p[(i + 1) % 4 + 4]); } // 面の一覧 var planes:Array = [ [p[0], p[1], p[2], p[3]], [p[7], p[6], p[5], p[4]], [p[0], p[4], p[5], p[1]], [p[1], p[5], p[6], p[2]], [p[2], p[6], p[7], p[3]], [p[3], p[7], p[4], p[0]] ]; // 面の中心のZ座標を求める var z:Dictionary= new Dictionary(); for(i = 0; i < planes.length; i++) { z[planes[i]] = (planes[i][0].z + planes[i][1].z + planes[i][2].z + planes[i][3].z) / 4; } // Zソート (奥のものから順番に並べる) planes.sort(function(a:Array, b:Array):Number { return z[b] - z[a]; }); // 奥から順番に面を描画 var counter:int = 0; for each(var plane:Array in planes) { drawPlane(g, plane[0], plane[1], plane[2], plane[3]); } } private function drawPlane(g:Graphics, p1:Vector3D, p2:Vector3D, p3:Vector3D, p4:Vector3D):void { // 単位法線ベクトル var v1:Vector3D = p2.subtract(p1); var v2:Vector3D = p4.subtract(p1); // ベクトルの外積(2つのベクトルに垂直なベクトル) var n:Vector3D= cross(v1, v2); //単位ベクトル(長さ1のベクトル)に変換 n.normalize(); // 光源の方向ベクトルとの内積 var l:Vector3D = new Vector3D(0, 0, -1); //l と n の内積を面の明るさとする(ランバートの法則) var brightness:Number = dot(n, l); // 面を塗る var b:int = 0x3f * brightness + 0xc0; g.beginFill(b * 0x10000 + b * 0x100 + b, 0.2); g.lineStyle(0, 0x666666); var p:Vector3D; p = p1.clone(); g.moveTo(p.x, p.y); p = p2.clone(); g.lineTo(p.x, p.y); p = p3.clone(); g.lineTo(p.x, p.y); p = p4.clone(); g.lineTo(p.x, p.y); g.endFill(); } private function drawPoint(g:Graphics, p:Vector3D):void { g.beginFill(0x000000); g.drawCircle(p.x, p.y, 3); g.endFill(); } private function drawLine(g:Graphics, p1:Vector3D, p2:Vector3D):void { g.beginFill(0, 0); g.lineStyle(1, 0x000000); g.moveTo(p1.x, p1.y); g.lineTo(p2.x, p2.y); g.lineStyle(); g.endFill(); } // 外積 private function cross(p1:Vector3D, p2:Vector3D):Vector3D { return new Vector3D(p1.y * p2.z - p1.z * p2.y, p1.z * p2.x - p1.x * p2.z, p1.x * p2.y - p1.y * p2.x); } // 内積 private function dot(p1:Vector3D, p2:Vector3D):Number { return p1.x * p2.x + p1.y * p2.y + p1.z * p2.z; } } forked from: Astro Cube