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

Saturn 3D, 토성, 고리 중첩 해결한 것

토성 예제.
@author Yongho, Ji
@since 2009.07.07
@see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
@see http://blog.jidolstar.com/547
Get Adobe Flash player
by jidolstar 02 Oct 2009
/**
 * Copyright jidolstar ( http://wonderfl.net/user/jidolstar )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/kpQ2
 */

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.GraphicsTrianglePath;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.PerspectiveProjection;
	import flash.geom.Rectangle;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.ui.Keyboard;
	import flash.utils.getTimer;

    [SWF(frameRate=32, backgroundColor=0x000000)]
	
	/**
	 * 토성 예제.
	 * @author Yongho, Ji
	 * @since 2009.07.07
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
	 * @see http://blog.jidolstar.com/547
	 */ 	
	public class Saturn3D extends Sprite
	{
		// 투영된 Vertex 정보 
		private var projected:Vector.<Number> = new Vector.<Number>(0, false);
		
		// 투영
		private var projection:PerspectiveProjection = new PerspectiveProjection();
		
		// World 변환행렬
		private var world:Matrix3D = new Matrix3D;
		
		// View Port (3D 렌더링 대상)
		private var viewport:Shape = new Shape();
		
		// 텍스쳐 정보 : 토성표면, 고리를 여기에 모두 합침 
		private var textureInfo:TextureInfo;
		
		// Mesh 데이타 : 토성표면, 고리 포함 
		private var mesh:GraphicsTrianglePath;

		// Viewport의 Z축 위치 
		private var viewPortZAxis:Number = 200;
		
		// triangle을 보여줄지 여부  
		private var visibleTriangle:Boolean = false;		
		
		/**
		 * 생성자 
		 */ 
		public function Saturn3D()
		{
			super();
			
			//화면 설정 
			//stage.align = StageAlign.TOP_LEFT;
			//stage.scaleMode = StageScaleMode.NO_SCALE;
			//stage.quality = StageQuality.BEST;

			//Texture 만들기 
			var faceTexture:BitmapData = new BitmapData( 400, 200, false );
			var ringTexture:BitmapData = new BitmapData( 60, 30, false );
			faceTexture.perlinNoise(64, 64, 3, 0, true, true, 7, true);
			ringTexture.perlinNoise(64, 64, 3, 0, true, true, 7, true);
			textureInfo = createTexture( faceTexture, ringTexture );
			faceTexture.dispose();
			ringTexture.dispose();
			 
			//Mesh 데이타 
			mesh = createMesh( textureInfo ); 
			
			//viewport를 화면의 중심으로 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;
			addChild(viewport);
			
			//projection의 fieldOfView를  60으로 지정 
			projection.fieldOfView = 60;
			
			//이벤트 처리 							
			stage.addEventListener( Event.RESIZE, onResize );
			stage.addEventListener( Event.ENTER_FRAME, onEnterFrame );
			stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseEvent );			
			stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyEvent );
			
			//사용법 표시 
			var textField:TextField = new TextField();
			textField.multiline = true;
			textField.textColor = 0xffffff;
			textField.htmlText = "Show/Hide Triangles : Space Key<br>Rotation : Mouse<br>Zoom In/Out : Left/Right Key<br>Change the field of view : Up/Down Key";
			textField.autoSize = TextFieldAutoSize.LEFT;
			addChild( textField ); 			
		}
		
		/**
		 * 프레임 마다 처리. 
		 */ 
		private function onEnterFrame( event:Event ):void
		{
			world.identity(); //단위행렬로 전환 
			world.appendRotation( getTimer() * 0.0027, Vector3D.Z_AXIS );
			world.appendRotation( xRotation, Vector3D.X_AXIS );
			world.appendRotation( yRotation, Vector3D.Y_AXIS );
			world.appendTranslation(0, 0, viewPortZAxis); //이동 
			world.append(projection.toMatrix3D()); //투영 변환 적용 
			
			// mesh 데이터를  투영하여  projected 생성 
			// uvtData도 갱신된다. 갱신되는 데이터는 T값이다. 
			Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
			
			viewport.graphics.clear();
		
			// Triangle 라인을 그림 
			if( visibleTriangle )
			{
				viewport.graphics.lineStyle( 1, 0xff0000, 1 );
			}
			
			//Texture 입힌다.
			viewport.graphics.beginBitmapFill( textureInfo.texture, null, false, true );
			viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
		
		}
		
		/**
		 * 사이즈 변경시 처리  
		 */ 
		private function onResize( event:Event ):void
		{
			//viewport는 항상 화면의 중심에 위치하도록 처리 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}
		
		private var xRotation:Number = -73;
		private var yRotation:Number = 68;
		private var prevX:Number;
		private var prevY:Number;

		/**
		 * 마우스 이벤트 - 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 );
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_MOVE:
					if( event.buttonDown == false )
					{
						stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
						stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
						break;
					}
					var dx:Number = mouseX - prevX;
					var dy:Number = mouseY - prevY;
					yRotation += dx;
					xRotation += dy;
					//trace( xRotation, yRotation );
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_UP:
					stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					break;
			}	
		}
		
		/**
		 * 키보드 이벤트 - 확대/축소/삼각형 보이기,안보이기/Field Of View 조정 
		 */		
		private function onKeyEvent( event:KeyboardEvent ):void
		{
			trace( event.charCode );
			
			switch( event.keyCode )
			{
				//확대
				case Keyboard.RIGHT:
					viewPortZAxis += 10;
					break;
				//축소
				case Keyboard.LEFT:
					viewPortZAxis -= 10;
					break;
				//삼각형 보이기/안보기 
				case Keyboard.SPACE:
					visibleTriangle = !visibleTriangle
					break;
				//Field Of View 증가
				case Keyboard.UP:
					if( projection.fieldOfView < 179 )
					{ 
						projection.fieldOfView++;
					}				
					break; 
				//Field Of View 감소 
				case Keyboard.DOWN:
					if( projection.fieldOfView > 1 )
					{
						projection.fieldOfView--;
					}				
					break; 
				
			}
		}
						
	}
}


import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import flash.geom.Rectangle;

/**
 * 토성 모양의 텍스쳐 정보를 가지는 클래스  
 */ 
class TextureInfo
{
	public var texture:BitmapData;
	public var faceRect:Rectangle;
	public var ringRect:Rectangle;
	
	/**
	 * 생성자
	 * @param texture 텍스쳐가 담긴 BitmapData
	 * @param faceRect 텍스쳐에서 표면 영역
	 * @param ringRect 텍스쳐에서 고리 영역
	 */ 
	public function TextureInfo( texture:BitmapData, faceRect:Rectangle, ringRect:Rectangle )
	{
		this.texture = texture;
		this.faceRect = faceRect;
		this.ringRect = ringRect;	
	}
}

/**
 * 텍스쳐 생성 
 * @param faceTexture 표면의 BitmapData
 * @param ringTexture 고리의 BitmapData 
 * @return TexturInfo의 객체 
 */ 
function createTexture( faceTexture:BitmapData, ringTexture:BitmapData ):TextureInfo
{
	var texture:BitmapData;

	texture = new BitmapData( 
			Math.max( faceTexture.width, ringTexture.width ), 
			faceTexture.height + ringTexture.height, 
			true,
			0xff000000 
	);

	faceTexture.lock();
	ringTexture.lock();

	var facePixels:ByteArray ;
	var ringPixels:ByteArray;
	facePixels = faceTexture.getPixels( new Rectangle( 0, 0, faceTexture.width, faceTexture.height ) );
	ringPixels = ringTexture.getPixels( new Rectangle( 0, 0, ringTexture.width, ringTexture.height ) );
	facePixels.position = 0;
	ringPixels.position = 0;

	texture.setPixels( 
		new Rectangle( 0, 0, faceTexture.width, faceTexture.height ), 
		facePixels 
	);
	texture.setPixels( 
		new Rectangle( 0, faceTexture.height, ringTexture.width, ringTexture.height ), 
		ringPixels 
	);
	
	var faceRect:Rectangle = new Rectangle( 0, 0, faceTexture.width, faceTexture.height );
	var ringRect:Rectangle = new Rectangle( 0, faceTexture.height+1, ringTexture.width, ringTexture.height );
	
	faceTexture.unlock();
	ringTexture.unlock();
	
	return new TextureInfo( texture, faceRect, ringRect ); 
}

/**
 * 토성모양의 Mesh 데이타 
 * @param textureInfo 텍스쳐 정보 
 * @return Mesh 데이타 
 */ 
function createMesh( textureInfo:TextureInfo ):GraphicsTrianglePath
{
	var width:Number = textureInfo.texture.width;
	var height:Number =textureInfo.texture.height;
	var faceRect:Rectangle = textureInfo.faceRect;
	var ringRect:Rectangle = textureInfo.ringRect;

	var minU:Number;
	var maxU:Number;
	var minV:Number;
	var maxV:Number;
	
	//표면 Mesh 데이타 만들기 
	minU = faceRect.x / width;
	maxU = (faceRect.x + faceRect.width) / width;
	minV = faceRect.y / height;
	maxV = (faceRect.y + faceRect.height) / height;
	var faceMesh:GraphicsTrianglePath = createSphereMesh( 50, 24, 24, minU, maxU, minV, maxV );
	
	//고리 Mesh 데이타 만들기 
	minU = ringRect.x / width;
	maxU = (ringRect.x + ringRect.width) / width;
	minV = ringRect.y / height;
	maxV = (ringRect.y + ringRect.height) / height;
	var ringMesh:GraphicsTrianglePath = createRingMesh( 70, 20, 50, minU, maxU, minV, maxV );

	//고리 Mesh 데이타에서 Index 부분 조정 
	var deltaIndex:uint = faceMesh.vertices.length/3; //Vertex는 x,y,z 3개씩 묶이므로... ^^
	var length:uint = ringMesh.indices.length;
	for( var i:int = 0; i < length; i++ )
	{
		//아래와 같이 2개의 mesh가 합쳐지면 뒤에 붙는  ring부분의 index값이 변경되야한다.
		ringMesh.indices[i] += deltaIndex; 
	}
	
	//최종 Mesh 데이타 완성 
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath();
	mesh.vertices = faceMesh.vertices.concat( ringMesh.vertices );
	mesh.uvtData = faceMesh.uvtData.concat( ringMesh.uvtData );
	mesh.indices = faceMesh.indices.concat( ringMesh.indices );
	mesh.culling = TriangleCulling.NONE;
	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;
}

/**
 * 구 Mesh 데이터 작성 
 * @param radius 구의 반지름  
 * @param hDiv 수평 방향의 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */
function createSphereMesh( radius:Number, hDiv:uint, vDiv:uint, minU:Number = 0, maxU:Number = 1, minV:Number = 0, maxV:Number = 1 ):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.POSITIVE );
	
	for( var i:uint = 0; i <= hDiv; i++ )
	{
		var s1:Number = Math.PI * 2 * i / hDiv;
		for( var j:uint = 0; j <= vDiv; j++ )
		{
			var s2:Number = Math.PI * j / vDiv + Math.PI / 2;
			var cos1:Number = Math.cos( s1 );
			var sin1:Number = Math.sin( s1 );
			var cos2:Number = Math.cos( s2 );
			var sin2:Number = Math.sin( s2 ); 
			var x:Number = radius * cos2 * cos1;
			var y:Number = radius * cos2 * sin1;
			var z:Number = radius * sin2;  			
			mesh.vertices.push( x, y, z );	 
			mesh.uvtData.push( 
				minU + (maxU-minU) * (i / hDiv), 
				minV + (maxV-minV) * (j / vDiv), 
				1
			 );
			
			if( j < vDiv && i < hDiv )
			{
                var a:uint =  i      * (vDiv + 1) + j;
                var b:uint = (i + 1) * (vDiv + 1) + j;
                mesh.indices.push(a, a + 1, b, b + 1, b, a + 1); 
			} 
		}
	}	
	
    return mesh;
}

/**
 * 고리 Mesh 데이터 작성 
 * @param radius 고리 안쪽 반지름  
 * @param thickness 고리 두께
 * @param div 고리 조각수 
 * @return mesh 데이터 
 */
function createRingMesh( radius:Number, thickness:Number, div:uint, minU:Number = 0, maxU:Number = 1, minV:Number = 0, maxV:Number = 1 ):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.NONE );

	var s:Number = 0;
	var x1:Number = radius;
	var x2:Number = radius + thickness;
	var y1:Number = 0;
	var y2:Number = 0;
	var z:Number = 0;  		
	var cos:Number;
	var sin:Number;	
	var n:Number = 0;
	
	for( var i:uint = 0; i < div; i++ )
	{
		//Vertices
		mesh.vertices.push( 
			x1, y1, z, 
			x2, y2, z 
		);
		s = Math.PI * 2 * (i + 1) / div;
		cos = Math.cos( s );
		sin = Math.sin( s );
		x1 = radius * cos;
		x2 = (radius + thickness) * cos;
		y1 = radius * sin;
		y2 = (radius + thickness) * sin;
		mesh.vertices.push( 
			x1, y1, z, 
			x2, y2, z 
		);

		//UVT
		mesh.uvtData.push( 
			minU,maxV,1, 
			maxU,maxV,1, 
			minU,minV,1, 
			maxU,minV,1 
		);
		
		//Indices
		n = i * 4;
		mesh.indices.push( n, n+1, n+2, n+2, n+1, n+3 ); 			
	}	
	
    return mesh;
}