Forked from: hacker_lightningM's forked from: 3D ドーナッツ diff:3 forked from: forked from: 3D ドーナッツ martial forked:0favorite:0lines:591license : All rights reserved modified : 2009-11-03 23:58:37 Embed Tweet // forked from hacker_lightningM's forked from: 3D ドーナッツ // forked from muta244's 3D ドーナッツ package { import flash.display.*; import flash.events.*; import flash.text.*; import flash.ui.Keyboard; import flash.utils.getTimer; [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")] public class Donuts02 extends Sprite { private var _donuts:Donuts; private var _matrix:Matrix3D; private var _render:Render; private var _textField:TextField; private var _rx:Number = 0; private var _ry:Number = 0; private var _rz:Number = 0; private var _isAutoPlay:Boolean = false; private const STAGE_WIDTH:Number = stage.stageWidth; private const STAGE_HEIGHT:Number = stage.stageHeight; public function Donuts02() { configure(); var pi:Number = Math.PI; var start:int; var i:int; _donuts = new Donuts(16, 0.7); _matrix = new Matrix3D(); _render = new Render(graphics, _matrix); addTextField(); mainLoop(null); stage.addEventListener(MouseEvent.CLICK, changePlayMode); _textField.text = "Partitions : " + _donuts.numPartitions + "\n" + "Vertices : " + _donuts.vertices.length + "\n" + "Stretch : " + _donuts.stretch; } private function configure():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.showDefaultContextMenu = true; } private function addTextField():void { _textField = new TextField(); _textField.defaultTextFormat = new TextFormat( null, null, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.RIGHT); _textField.autoSize = TextFieldAutoSize.RIGHT; _textField.background = true; _textField.backgroundColor = 0x00FFFF; _textField.selectable = false; _textField.x = STAGE_WIDTH - _textField.width; addChild(_textField); var announce:TextField = new TextField(); announce.defaultTextFormat = new TextFormat( null, null, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.LEFT); announce.autoSize = TextFieldAutoSize.LEFT; announce.background = true; announce.backgroundColor = 0x000000; announce.selectable = false; announce.text = "\"DOWN\" : Partitions -\n" + "\"UP\" : Partitions +\n" + "\"LEFT\" : Stretch -\n" + "\"RIGHT\" : Stretch +\n" + "\"Z\" : Polygon on/off\n" + "\"X\" : Wireframe on/off"; announce.y = STAGE_HEIGHT - announce.height; addChild(announce); } private function mainLoop(event:Event):void { _matrix.identity(); _matrix.scale(50, 50, 50); _matrix.rotateX(_rx); _matrix.rotateY(_ry); _matrix.rotateZ(_rz); _matrix.translate(STAGE_WIDTH / 2, STAGE_HEIGHT / 2, 0); graphics.clear(); _render.drawIndexedPrimitive( Render.PRIMITIVE_POLYGON, _donuts.vertices, _donuts.indices); _rx += 0.04; _ry += 0.08; _rz += 0.02; } private function changePlayMode(event:MouseEvent):void { if (_isAutoPlay) { removeEventListener(Event.ENTER_FRAME, mainLoop); stage.removeEventListener(KeyboardEvent.KEY_DOWN, changeDonuts); } else { addEventListener(Event.ENTER_FRAME, mainLoop); stage.addEventListener(KeyboardEvent.KEY_DOWN, changeDonuts); } _isAutoPlay = !_isAutoPlay; } private function changeDonuts(event:KeyboardEvent):void { var numPartitions:int = _donuts.numPartitions; switch (event.keyCode) { case Keyboard.UP: if (numPartitions >= 0) { _donuts.numPartitions += 1; } break; case Keyboard.DOWN: if (numPartitions > 0) { _donuts.numPartitions -= 1; } break; case Keyboard.LEFT: _donuts.stretch -= 0.02; break; case Keyboard.RIGHT: _donuts.stretch += 0.02; break; case 88: //trace("X"); _render.wireframeEnabled = !_render.wireframeEnabled; break; case 90: //trace("Z"); _render.polygonEnabled = !_render.polygonEnabled; break; } _textField.text = "Partitions : " + _donuts.numPartitions + "\n" + "Vertices : " + _donuts.vertices.length + "\n" + "Stretch : " + _donuts.stretch.toString().substr(0, 4); } } } import flash.display.Graphics; class Render { public static const PRIMITIVE_POLYGON:int = 1; private var _graphics:Graphics; private var _matrix:Matrix3D; private var _mode:int; private var _polygonEnabled:Boolean = true; private var _wireframeEnabled:Boolean = false; // 光源ベクトル private var _light:Vector3D = new Vector3D( 0.0, 0.0, -1.0); // アンビエント(環境光)の色 private var _ambient:Vector3D = new Vector3D( 0.2, 0.2, 0.2); // ディフューズ(拡散光)の色 private var _diffuse:Vector3D = new Vector3D( 0.8, 0.8, 0.8); /** * 新しい Render インスタンスを作成します。 */ public function Render( graphics:Graphics = null, matrix:Matrix3D = null, mode:int = 1):void { _graphics = graphics; _matrix = matrix; _mode = mode; } public function drawIndexedPrimitive(type:int, vertices:Array, indices:Array):void { var i:int; var l:int; var tv:Array = []; // 座標を一次変換 for (i = 0, l = vertices.length; i < l; ++i) { tv.push(_matrix.transform(vertices[i])); } // ラスタライズ switch (type) { case PRIMITIVE_POLYGON: var triangles:Array = []; for (i = 0, l = indices.length; i < l; i += 3) { triangles.push(new Triangle3D( tv[indices[i ]], tv[indices[i + 1]], tv[indices[i + 2]] )); } // 深度ソート triangles.sortOn("depth", Array.DESCENDING | Array.NUMERIC); for each (var triangle:Triangle3D in triangles) { drawPolygon(triangle); } break; } } private function drawPolygon(triangle:Triangle3D):void { if (polygonEnabled) { // 面の法線ベクトルを求める。 var n:Vector3D = triangle.normal(); // 光源と法線の内積を取る。 var w:Number = n.dot(_light); // 背面カリング処理。 // 内積が負なら背面なので描画しない。(ただしパースをつけた場合は正確な判定方法ではない。) if (w < 0) { return; } var r:int = (_ambient.x + w * _diffuse.x) * 255; var g:int = (_ambient.y + w * _diffuse.y) * 255; var b:int = (_ambient.z + w * _diffuse.z) * 255; r = (r < 0) ? 0 : (r > 255) ? 255 : r; g = (g < 0) ? 0 : (g > 255) ? 255 : g; b = (b < 0) ? 0 : (b > 255) ? 255 : b; _graphics.beginFill(r << 16 | g << 8 | b); } if (wireframeEnabled) { _graphics.lineStyle(0, 0xFF0000); } _graphics.moveTo(triangle.v1.x, triangle.v1.y); _graphics.lineTo(triangle.v2.x, triangle.v2.y); _graphics.lineTo(triangle.v3.x, triangle.v3.y); _graphics.lineTo(triangle.v1.x, triangle.v1.y); _graphics.endFill(); } public function get graphics():Graphics { return _graphics; } public function set graphics(value:Graphics):void { _graphics = value; } public function get matrix():Matrix3D { return _matrix; } public function set matrix(value:Matrix3D):void { _matrix = value; } public function get polygonEnabled():Boolean { return _polygonEnabled; } public function set polygonEnabled(value:Boolean):void { _polygonEnabled = value; } public function get wireframeEnabled():Boolean { return _wireframeEnabled; } public function set wireframeEnabled(value:Boolean):void { _wireframeEnabled = value; } } class Donuts { private var _vertices:Array; private var _indices:Array; private var _numPartitions:int; private var _stretch:Number; /** * @param numPartitions 分割数 * @param stretch 拡がり */ public function Donuts(numPartitions:int = 16, stretch:Number = 1):void { _numPartitions = numPartitions; _stretch = stretch; setup(numPartitions, stretch); } /** * @private */ private function setup(numPartitions:int, stretch:Number):void { var n:int = numPartitions; var i:int; var j:int; var a:Number = 0; var c:Number = 0; var addc:Number = Math.PI * 2 / n; var s:Number; var z:Number; var x:Number; var y:Number; _vertices = []; for (i = 0; i < n; ++i) { s = Math.sin(c) + 2; z = Math.cos(c); for (j = 0; j < n; ++j) { x = Math.cos(a); y = Math.sin(a); _vertices.push(new Vector3D( x * s * stretch, y * s * stretch, z * stretch )); a += Math.PI / n * 2; } c += addc; } var m:int = n * n; var off:Number; _indices = []; for (i = 0; i < n; ++i) { off = i * n; for (j = 0; j < n; ++j) { _indices.push((off + n) % m + (j + 1) % n); _indices.push( off + (j + 1) % n); _indices.push( off + j ); _indices.push((off + n) % m + j ); _indices.push((off + n) % m + (j + 1) % n); _indices.push( off + j ); } } } public function get vertices():Array { return _vertices.concat(); } public function get indices():Array { return _indices.concat(); } public function get numPartitions():int { return _numPartitions; } public function set numPartitions(value:int):void { _numPartitions = value; setup(value, stretch); } public function get stretch():Number { return _stretch; } public function set stretch(value:Number):void { _stretch = value; setup(numPartitions, value); } } class Triangle3D { private var _v1:Vector3D; private var _v2:Vector3D; private var _v3:Vector3D; /** * 新しい Triangle3D インスタンスを作成します。 */ public function Triangle3D(v1:Vector3D, v2:Vector3D, v3:Vector3D):void { _v1 = v1; _v2 = v2; _v3 = v3; } public function get v1():Vector3D { return _v1; } public function set v1(value:Vector3D):void { _v1 = value; } public function get v2():Vector3D { return _v2; } public function set v2(value:Vector3D):void { _v2 = value; } public function get v3():Vector3D { return _v3; } public function set v3(value:Vector3D):void { _v3 = value; } /** * 各ベクトルの z の値を比較し、最も低い数値を返します。 * 深度ソート時に Array オブジェクトの sortOn と合わせて使用します。 */ public function get depth():Number { var min:Number = (v1.z < v2.z) ? v1.z : v2.z; return (min < v3.z) ? min : v3.z; } /** * このトライアングルを面とする法線ベクトルを返します。 * * @return このトライアングルを面とする法線ベクトル */ public function normal():Vector3D { var n:Vector3D = new Vector3D(); n.cross(v1.distance(v2), v1.distance(v3)); n.normalize(); return n; } /** * このトライアングルのストリング表現を返します。 * * @return このトライアングルのストリング表現 */ public function toString():String { var temp:String = "Triangle3D {\n" + "\tv1 : " + v1 + "\n" + "\tv2 : " + v2 + "\n" + "\tv3 : " + v3 + "\n}"; return temp; } } class Matrix3D { private var _matrix:Array; /** * 新しい Matrix3D インスタンスを作成します。 */ public function Matrix3D():void { identity(); } public function identity():void { _matrix = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; } public function scale(sx:Number, sy:Number, sz:Number) { _matrix = _multiply(_matrix, [ sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1 ]); } public function translate(dx:Number, dy:Number, dz:Number) { _matrix = _multiply(_matrix, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, dx, dy, dz, 1 ]); } public function rotateX(angleRadians:Number) { var sin = Math.sin(angleRadians); var cos = Math.cos(angleRadians); _matrix = _multiply(_matrix, [ 1, 0, 0, 0, 0, cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1 ]); } public function rotateY(angleRadians:Number) { var sin = Math.sin(angleRadians); var cos = Math.cos(angleRadians); _matrix = _multiply(_matrix, [ cos, 0, -sin, 0, 0, 1, 0, 0, sin, 0, cos, 0, 0, 0, 0, 1 ]); } public function rotateZ(angleRadians:Number) { var sin = Math.sin(angleRadians); var cos = Math.cos(angleRadians); _matrix = _multiply(_matrix, [ cos, sin, 0, 0, -sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } /** * このマトリクスで指定のベクトルを一次変換します。 * * @return 一次変換されたベクトル */ public function transform(v:Vector3D):Vector3D { var temp:Vector3D = new Vector3D(); var m:Array = _matrix; temp.x = m[0] * v.x + m[4] * v.y + m[ 8] * v.z + m[12]; temp.y = m[1] * v.x + m[5] * v.y + m[ 9] * v.z + m[13]; temp.z = m[2] * v.x + m[6] * v.y + m[10] * v.z + m[14]; return temp; } /** * このマトリクスと指定マトリクスの成分を乗算(合成)します。 * * @param m 対象の Matrix3D オブジェクト */ public function multiply(m:Matrix3D):void { _matrix = _multiply(m.toArray(), _matrix); } /** * @private */ private function _multiply(m1:Array, m2:Array):Array { var temp:Array = []; var i:int = 0; for (var y:int = 0; y < 4; ++y) { for (var x:int = 0; x < 4; ++x) { temp[x + i] = m1[i ] * m2[x ] + m1[i + 1] * m2[x + 4] + m1[i + 2] * m2[x + 8] + m1[i + 3] * m2[x + 12]; } i += 4; } return temp; } /** * このマトリクスの成分配列を返します。 * * @return このマトリクスの成分配列 */ public function toArray():Array { return _matrix.concat(); } /** * このマトリクスのストリング表現を返します。 * * @return このマトリクスのストリング表現 */ public function toString():String { var temp:String = "Matrix3D [\n"; for (var i:int = 0, l:int = _matrix.length; i < l; ++i) { if (i % 4 == 0) { if (i != 0) { temp += "\n"; } temp += "\t"; } if (i != l - 1) { temp += _matrix[i] + ", "; } else { temp += _matrix[i] + "\n]"; } } return temp; } } class Vector3D { private var _x:Number; private var _y:Number; private var _z:Number; /** * 新しい Vector3D インスタンスを作成します。 */ public function Vector3D(x:Number = 0, y:Number = 0, z:Number = 0):void { _x = x; _y = y; _z = z; } public function get x():Number { return _x; } public function set x(value:Number):void { _x = value; } public function get y():Number { return _y; } public function set y(value:Number):void { _y = value; } public function get z():Number { return _z; } public function set z(value:Number):void { _z = value; } /** * このベクトルを正規化して単位ベクトルにします。 * 単位ベクトルとは大きさが 1 のベクトルのことです。 */ public function normalize():void { var l:Number = 1 / Math.sqrt(x * x + y * y + z * z); x *= l; y *= l; z *= l; } /** * 指定された 2 つのベクトルの外積をこのベクトルに設定します。 * 外積は主に、面に垂直な法線ベクトルを求めるために使用します。 * * @param v1 1 つ目のベクトル * @param v1 2 つ目のベクトル */ public function cross(v1:Vector3D, v2:Vector3D):void { x = v1.y * v2.z - v1.z * v2.y; y = v1.z * v2.x - v1.x * v2.z; z = v1.x * v2.y - v1.y * v2.x; } /** * このベクトルと指定ベクトルの内積を返します。 * 内積を取るとベクトルとベクトルのなす角度が求まります。 * * @param v ベクトル * @return このベクトルと指定ベクトルの内積 */ public function dot(v:Vector3D):Number { return x * v.x + y * v.y + z * v.z; } /** * 指定された 2 つのベクトルの内積を返します。 * 内積を取るとベクトルとベクトルのなす角度が求まります。 * * @param v1 1 つ目のベクトル * @param v1 2 つ目のベクトル * @return 指定された 2 つのベクトルの内積 */ public static function dot(v1:Vector3D, v2:Vector3D):Number { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } /** * ベクトルを反転します。 */ public function invert():void { x *= -1; y *= -1; z *= -1; } /** * このベクトルと指定ベクトルとの差分を表すベクトルを返します。 * * @return 差分を表すベクトル */ public function distance(v:Vector3D):Vector3D { return new Vector3D(x - v.x, y - v.y, z - v.z); } /** * このベクトルのコピーを返します。 * * @return このベクトルのコピー */ public function clone():Vector3D { return new Vector3D(x, y, z); } /** * このベクトルのストリング表現を返します。 * * @return このベクトルのストリング表現 */ public function toString():String { return "Vector3D {x:" + x + ", y:" + y + ", z:" + z + "}"; } } Code Fullscreen Preview Fullscreen Math.cos toString Boolean type Math.min KeyboardEvent.KEY_DOWN clone TextFieldAutoSize.RIGHT lineTo transform Array.DESCENDING TextFormatAlign.RIGHT TextFormatAlign.LEFT backgroundColor addEventListener Math.sin background Keyboard.DOWN Keyboard.UP Keyboard.RIGHT sort new page view favorite forked pv103 forked from: forked from: fork.. mikako forked:0 favorite:0lines:591 (diff:1)