In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Geodesic Sphere using drawTriangle

Geodesic Sphere using drawTriangle()
@author Yongho Ji (jidolstar@gmail.com)
@since 2009.09.16
/**
 * Copyright jidolstar ( http://wonderfl.net/user/jidolstar )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/gw52
 */

package {
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.text.*;
	import flash.ui.*;
	
	[SWF(frameRate=24, backgroundColor=0x000000)]
	/**
	 * Geodesic Sphere using drawTriangle()
	 * @author Yongho Ji (jidolstar@gmail.com)
	 * @since 2009.09.16
	 */ 
	public class GeodesicSphereUsingDrawTriangle extends Sprite {
		
		//투영된 Vectex 정보 
		private var projected:Vector.<Number>;
		//World 변환행렬
		private var world:Matrix3D;
		//투영을 위한 변환행렬		
		private var projection:Matrix3D;
		//Mesh 데이터 
		private var mesh:GraphicsTrianglePath;
		//ViewPort (3D 렌더링 대상)
		private var viewport:Shape;
		//반경
		private var radius:Number = 200;
		//나눌수
		private var fractures:int = 15;	
		//회전각 
		private var zRotation:Number = 0;
		private var xRotation:Number = -125;
		//Z축 위치
		private var zPosition:Number = -10;
		//Temp : 이전값		
		private var prevX:Number;
		private var prevY:Number;
		//Temp : 마우스로 회전시 	
		private var rotating:Boolean = false;	
		private var fracturesChanged:Boolean = false;
		private var rendering:Boolean = true;
		
		/**
		 * 생성자 
		 */ 
		public function GeodesicSphereUsingDrawTriangle() {
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			
			//Viewport 
			addChild( viewport = new Shape() );
			
			//투영 변환 행렬
			var p:PerspectiveProjection = new PerspectiveProjection();
			p.fieldOfView = 30;
			projection = p.toMatrix3D();
			
			//World 변환행렬 
			world = new Matrix3D();
			
			//Mesh 데이타 
			//radius = Utils3D.projectVector( projection, new Vector3D(1.0,0,1.0) ).x/4;
			zPosition = radius;
			mesh = createGeodesicSphereMesh( radius, fractures ); 
			projected = new Vector.<Number>(0,false);
			
			//이벤트 핸들러 등록 
			stage.addEventListener( Event.ENTER_FRAME, render );
			stage.addEventListener( Event.RESIZE, arrange ); 
			stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseEvent );
			stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );
			stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );
			
			//배치 
			arrange();

			var textField:TextField = new TextField();
			textField.textColor = 0xffffff;
			textField.autoSize = TextFieldAutoSize.LEFT;
			textField.text = "up key : increase fractures \ndown key : decrease fracture\nmouse drag : rotation";
			addChild( textField );
		        
		}

		/**
		 * 중앙에 배치 
		 */ 
		private function arrange( event:Event = null ):void {
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}		
		
		/**
		 * 렌더링 
		 */ 
		private function render( event:Event = null ):void {
			if( rotating ) {
				var dx:Number = mouseX - prevX;
				var dy:Number = mouseY - prevY;
				zRotation += dx;
				xRotation -= dy;
				//if( xRotation > 90 ) xRotation = 90;
				//if( xRotation < -90 ) xRotation = -90;
				prevX = mouseX;
				prevY = mouseY;	
				rendering = true;			
			}
			if( fracturesChanged ) {
				fracturesChanged = false;
				mesh = createGeodesicSphereMesh( radius, fractures );
				projected = new Vector.<Number>(0,false);
				rendering = true; 
			}
			if( rendering )	{
				rendering = false;
				world.identity();
				world.appendRotation( zRotation, Vector3D.Z_AXIS );
				world.appendRotation( xRotation, Vector3D.X_AXIS );
				world.appendTranslation( 0, 0, zPosition );
				Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
				viewport.graphics.clear();
				viewport.graphics.lineStyle( 1, 0xff0000, 0.5 );
				//텍스쳐를 가지고 있다면 아래 주석을 풀고 Test 한다.
				//viewport.graphics.beginBitmapFill( texture, null, false, true );
				viewport.graphics.drawTriangles( projected, mesh.indices, mesh.uvtData, mesh.culling );
				//viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );  
				viewport.graphics.endFill();
			}		
		}
		
		/**
		 * 마우스 휠 처리 
		 */ 
		private function onMouseWheel( event:MouseEvent ):void {
			zPosition += event.delta * 50;
		}		
		
		/**
		 * 마우스 이벤트 - x,y축 회전
		 */ 
		private function onMouseEvent( event:MouseEvent ):void {
			switch( event.type )
			{
				case MouseEvent.MOUSE_DOWN:
					stage.addEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.addEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					rotating = true;
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_UP:
					stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					rotating = false;
					break;
			}	
		}
		
		private function onKeyDown( event:KeyboardEvent ):void {
			switch( event.keyCode )
			{
				case Keyboard.UP:
					fractures++;
					if( fractures > 25 ) fractures = 25;
					fracturesChanged=true;
					break;
				case Keyboard.DOWN:
					fractures--;
					if( fractures < 0 ) fractures = 0;
					fracturesChanged=true;
					break;
			}
		}
	}
}

import flash.display.*;

/**
 * Geodesic Sphere Mesh 데이타 만듬 
 * @param radius 반경 
 * @param fractures 나눌 수 
 */ 
function createGeodesicSphereMesh( radius:Number, fractures:Number ):GraphicsTrianglePath {
	var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
	var indices:Vector.<int> = new Vector.<int>( 0, false );
	var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.NEGATIVE );
	
	var D2R:Number = Math.PI / 180;	//Degree->Radian
	var TPI:Number = Math.PI * 2;
	var PI:Number = Math.PI;
	var HPI:Number = Math.PI / 2;
	var hnDE:int = fractures + 1; 	//적위 방향 쪼갠수/2
	var nDE:int = 2 * hnDE;			//적위 방향 쪼갠수 
	var nRA:int;					//적위에 대한 적경 방향 쪼갠수 
	var ra:Number;					//적경 (단위:라디안)
	var dec:Number; 				//적도(단위:라디안)
	var dDE:Number = 180/nDE*D2R;	//적위 간격(단위:라디안) 
	var dRA:Number;					//적도  간격(단위:라디안)
	var i:int;
	var j:int;
	var x:Number;
	var y:Number;
	var z:Number;
	var sinDE:Number;
	var cosDE:Number;
	var sinRA:Number;
	var cosRA:Number;
	var u:Number;
	var v:Number;
	
	
	////////////////////////////////
	// Vertex, UVT 데이타 만들기
	////////////////////////////////
	
	// 적위 -90->0 : 
	vertices.push( 0, 0, -radius );
	uvtData.push( 0, 0, 1 );
	for( i = 0; i < hnDE; i++ ) {
		nRA = 4*(i+1); 
		dRA = 360 / nRA * D2R;
		dec = -HPI+(i+1)*dDE;
		v = (HPI+dec)/PI;
		sinDE = Math.sin( dec );
		cosDE = Math.cos( dec );
		z = radius * sinDE;
		for( j = 0; j <= nRA; j++ ) {
			ra = j * dRA;
			sinRA = Math.sin( ra );
			cosRA = Math.cos( ra );
			x = radius * cosDE * cosRA;
			y = radius * cosDE * sinRA;
			u = ra / TPI;
			vertices.push( x, y, z );
			uvtData.push( u, v, 1 );
		}
	}
	
	// 적위 0 -> 90 
	for( i = 1; i < hnDE; i++ ) {
		nRA = 4*(hnDE-i);
		dRA = 360 / nRA * D2R;	
		dec = dDE * i;
		v = (HPI+dec)/PI;
		sinDE = Math.sin( dec );
		cosDE = Math.cos( dec );
		z = radius * sinDE;
		for( j = 0; j <= nRA; j++ ) {
			ra = j * dRA;
			sinRA = Math.sin( ra );
			cosRA = Math.cos( ra );
			x = radius * cosDE * cosRA;
			y = radius * cosDE * sinRA;
			u = ra / TPI;
			vertices.push( x, y, z );
			uvtData.push( u, v, 1 );
		}
	}
	vertices.push( 0, 0, radius );
	uvtData.push( 0, 1, 1 );
	
	////////////////////////////////
	// Vetex에 대한 index 만들기 
	// 폴리곤을 생성하기 위한 기초작업이다.
	////////////////////////////////
	
	var k:int;
	var pt0:int, pt1:int, pt2:int;	//하나의 폴리곤을 생성하는 Vertex의 index값 
	var u_idx_start:int, u_idx_end:int, u_idx:int; //상단 index 
	var l_idx_start:int, l_idx_end:int, l_idx:int; //하단 index
	var isUp:int;	//지그재그로 컬링 폴리곤을 생성하기 위해 
	var tris:int; 	//한개 분면에서 해당 적위에 대한 면의 수 
	var triIdx:int; //한개 분면에서 해당 적위에 대한 면의 수만큼 index를 증가하기 위해 사용 
	
	//적위 -90->0    	
	tris = 1;
	u_idx_start = 0; 
	u_idx_end = 0;
	for( i = 0; i < hnDE; ++i ) {
		//적위 간격으로 상하  시작index와 끝 index를 지정 
		l_idx_start = u_idx_start;
		l_idx_end = u_idx_end;
		u_idx_start	+= 4*i + 1; 
		u_idx_end 	+= 4*(i+1)+1;
		l_idx = l_idx_start;
		u_idx = u_idx_start;
		
		//4분면을 따라 Face를 만들도록 한다. 
		for( k = 0; k < 4; ++k ) {
			isUp = 1;
			//한개 분면에 대한 Face의 index를 만들어준다. 
			for( triIdx = 0; triIdx < tris; ++triIdx ) {
				if( isUp === 1) {
					pt0 = l_idx;
					pt2 = u_idx;
					u_idx++;
					pt1 = u_idx;
					isUp = 0;
				} else {
					pt0 = u_idx;
					pt1 = l_idx;
					l_idx++;
					pt2 = l_idx;
					isUp = 1;
				}
				indices.push( pt0, pt1, pt2 );
			} 
		}
		tris += 2; //한개의 분면에서 해당 적위에 대한 면의 수는 2씩 증가한다. 
	}
	
	//적위 0 -> 90     	
	for( i = hnDE-1; i >= 0; i-- ){
		l_idx_start = u_idx_start;
		l_idx_end = u_idx_end;
		u_idx_start = u_idx_start + 4*(i+1)+1;
		u_idx_end = u_idx_end + 4*i + 1;
		tris -= 2;
		u_idx = u_idx_start;
		l_idx = l_idx_start;
		for( k = 0; k < 4; ++k ) {
			isUp = 0;
			for( triIdx = 0; triIdx < tris; triIdx++ ) {
				if( isUp === 1) {
					pt0 = l_idx;
					pt2 = u_idx;
					u_idx++;
					pt1 = u_idx;
					isUp = 0;
				} else {
					pt0 = u_idx;
					pt1 = l_idx;
					l_idx++;
					pt2 = l_idx;
					isUp = 1;
				}
				indices.push( pt0, pt1, pt2 );
			}
		}
	}
	
	
	return mesh;
}

/**
 * GraphicsTrianglePath를 기반으로, Z축으로 sort된 인덱스를 돌려준다.
 * 이 작업을 해주어야 z축 깊이에 따라 Triangle이 제대로 그려진다. 
 * @param mesh 정보 
 * @return sort된 index 데이터 
 */
function getSortedIndices( mesh:GraphicsTrianglePath ):Vector.<int> {
    var triangles:Array = [];
    var length:uint = mesh.indices.length;
    
    //z축 sort를 위한 기반 제작 
    for ( var i:uint=0; i < length; i += 3 ) {
        var i1:uint = mesh.indices[ i+0 ];
        var i2:uint = mesh.indices[ i+1 ];
        var i3:uint = mesh.indices[ i+2 ];
        var z:Number = Math.min( mesh.uvtData[i1 * 3 + 2], mesh.uvtData[i2 * 3 + 2], mesh.uvtData[i3 * 3 + 2] );
        if (z > 0) { 
        	triangles.push({i1:i1, i2:i2, i3:i3, z:z}); 
        }
    }
    
    //z축으로 sort
    triangles = triangles.sortOn("z", Array.NUMERIC);
    
    //sort된 값을 이용해 Vector값 만듬 
    var sortedIndices:Vector.<int> = new Vector.<int>(0, false);
    for each (var triangle:Object in triangles) {
        sortedIndices.push(triangle.i1, triangle.i2, triangle.i3);
    }
    return sortedIndices;
}