forked from: 3D Example, Flash Player 10 : flash on 2009-7-2

by dotf forked from 3D Example, Flash Player 10 : flash on 2009-7-2 (diff: 2)
3D Texture 예제.
@author Yongho, Ji
@since 2009.07.01
@see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
♥0 | Line 238 | Modified 2009-11-13 16:43:51 | MIT License
play

ActionScript3 source code

/**
 * Copyright dotf ( http://wonderfl.net/user/dotf )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1T0N
 */

// forked from jidolstar's 3D Example, Flash Player 10 : flash on 2009-7-2
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.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.PerspectiveProjection;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;
	import flash.text.TextField;
        import flash.text.TextFieldAutoSize;
	
    [SWF(frameRate=60, backgroundColor=0x000000)]
	
	/**
	 * 3D Texture 예제.
	 * @author Yongho, Ji
	 * @since 2009.07.01
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
	 */ 
	public class Texture3D extends Sprite
	{
		// 투영된  Vertex 정보 
		private var projected:Vector.<Number>;

        // 투영
        private var projection:PerspectiveProjection = new PerspectiveProjection();
        
        // World 변환 행렬 
        private var world:Matrix3D = new Matrix3D();
		
		// Viewport (3D 렌더링 대상)
		private var viewport:Shape = new Shape();
		
		// Mesh 데이터 
		private var mesh:GraphicsTrianglePath
		
		// Texture
		private var texture:BitmapData = new BitmapData(512, 512, false);
		
		//triangle을 보여줄지 여부  
		private var visibleTriangle:Boolean = false;
		
		//Texture를 보여줄지 여부 
		private var visibleTexture:Boolean = true;
		
		//실린더  Mesh번호 
		private const MESH_CYLINDER:int = 1;
		
		//원형체  Mesh번호  
		private const MESH_TORUS:int = 2;
		
		//구 Mesh 번호 
		private const MESH_SPHERE:int = 3;
		
		//선택한 Mesh 번호 
		private var selectedMesh:int;
		
		//Viewport의 Z축 위치 
		private var viewPortZAxis:Number = 300;
		
		/**
		 * 생성자 
		 */ 
		public function Texture3D()
		{
			super();
			
			//화면 설정 
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.BEST;

                        //텍스쳐 입히기 
                        texture.perlinNoise(64, 64, 3, 0, true, true, 7, true);
			
			//viewport를 화면의 중심으로 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;
			addChild(viewport);
			
			//projection의 fieldOfView를  60으로 지정 
			projection.fieldOfView = 20;
			
			//mesh 데이터 설정 
			selectMesh( MESH_CYLINDER );
			
			//이벤트 처리 							
			stage.addEventListener( Event.RESIZE, onResize );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
			stage.addEventListener( MouseEvent.CLICK, onMouseClick );
			stage.addEventListener( KeyboardEvent.KEY_DOWN, onKey );
			stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );

 			var textField:TextField = new TextField();
			textField.multiline = true;
			textField.textColor = 0xffffff;
			textField.htmlText = "Change Shape : Key 1~3 <br>Show Texture : Key T<br>Show Triangle : Mouse Click<br>Zoom In/Out : Mouse Wheel";
			textField.autoSize = TextFieldAutoSize.LEFT;
			addChild( textField ); 
 
		}
		
		/**
		 * Mesh를 선택한다.
		 */ 
		private function selectMesh( meshNumber:int ):void
		{
			if( selectedMesh == meshNumber ) return;
			selectedMesh = meshNumber;
			
			//투영결과 Vertex 데이타를 초기화시킨다. 
			//이것을 해야 Mesh데이터가 바뀔때마다 drawTriangle에서 에러 발생 안함 
			projected  = new Vector.<Number>(0, false);
			switch( selectedMesh )
			{
				//실린더 
				case MESH_CYLINDER:
					mesh = createCylinderMesh( 50, 100, 20, 5 );
					break;
				//원형체 
				case MESH_TORUS:
					mesh = createTorusMesh( 50, 25, 32, 16 );
					break;
				//구 
				case MESH_SPHERE:
					mesh = createSphereMesh( 50, 32, 32 );
					break;
			}			
		}
		
		/**
		 * 사이즈 변경시 처리  
		 */ 
		private function onResize( event:Event ):void
		{
			//viewport는 항상 화면의 중심에 위치하도록 처리 
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}
		
		/**
		 * 프레임 마다 처리. 
		 */ 
		private function onEnterFrame( event:Event ):void
		{
            world.identity(); //단위행렬로 전환 
            world.appendRotation( getTimer() * 0.027, Vector3D.X_AXIS ); //X축 회전
            world.appendRotation( getTimer() * 0.061, Vector3D.Y_AXIS ); //Y축 회전 
            world.appendTranslation(0, 0, viewPortZAxis); //이동 
            world.append(projection.toMatrix3D()); //투영 변환 적용 
            
            // mesh 데이터를  투영하여  projected 생성 
            // uvtData도 갱신된다. 갱신되는 데이터는 T값이다. 
            Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
            
            // texture를 이용해 렌더링
            viewport.graphics.clear();
            
            // Triangle 라인을 그림 
            if( visibleTriangle )
            {
            	viewport.graphics.lineStyle( 1, 0xff0000, 1.0 );
            }
            
            //Texture 입힌다.
            if( visibleTexture )
            {
            	viewport.graphics.beginBitmapFill( texture, null, false, true );
                viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
            }
            else
            {
            	viewport.graphics.beginFill( 0x00ccff, 1.0 );
                viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), null, mesh.culling );            	
	            viewport.graphics.endFill();			
            }
		}
		
		/**
		 * 마우스 클릭 처리 
		 */ 
		private function onMouseClick( event:MouseEvent ):void
		{
			//삼각형  선 Visible 바꿈 
			visibleTriangle = !visibleTriangle;	
		}
		
		/**
		 * 키보드 이벤츠 처리  
		 */ 
		private function onKey( event:KeyboardEvent ):void
		{
			if( event.charCode == 116 )
			{
				visibleTexture = !visibleTexture;	
			}
			else
			{
				selectMesh( event.charCode - 48 ); //1,2...
			}
		}
		
		/**
		 * 마우스 휠 처리 
		 */ 
		private function onMouseWheel( event:MouseEvent ):void
		{
			viewPortZAxis += event.delta * 10;
		}
	}
}

import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;

/**
 * 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 height 원통의 높이 
 * @param hDiv 반지름 방향으로 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */ 
function createCylinderMesh( radius:Number, height:Number, hDiv:uint, vDiv:uint ):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 );
	
	for( var i:uint = 0; i <= hDiv; i++ )
	{
		var theta:Number = Math.PI * 2 * i / hDiv; //z축에서 y축으로 잰각 
		for( var j:uint = 0; j <= vDiv; j++ )
		{
			var x:Number = -height/2 + j * height/vDiv;
			var y:Number = radius * Math.sin( theta );
			var z:Number = radius * Math.cos( theta );
			mesh.vertices.push( x, y, z );				//하나의 Vertex 데이타 
			mesh.uvtData.push( i / hDiv, j / vDiv, 1 ); //하나의 UVT 데이타 . Texture의 점과 Vectex를 일치시켜 Texture를 입히기 위한 데이타이다.
			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); //삼각형의 index이다. culling방향 고려 
			} 
		}
	}
	return mesh;
}

/**
 * 원환체(도너츠모양) Mesh 데이터 작성 
 * @param hRadius 원환체의 수평축 반지름
 * @param vRadius 원환체의 수직축 반지름  
 * @param hDiv 수평 방향의 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */
function createTorusMesh( hRadius:Number, vRadius:Number, hDiv:uint, vDiv:uint ):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 );
	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 * 2 * j / vDiv;
            var r:Number = Math.cos(s2) * vRadius + hRadius;
			var x:Number = Math.cos(s1) * r;
			var y:Number = Math.sin(s1) * r;
			var z:Number = Math.sin(s2) * vRadius;            
            mesh.vertices.push( x, y, z );	
            mesh.uvtData.push(i / hDiv, 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(b, a + 1, a, a + 1, b, b + 1);
            }
        }
    }
    return mesh;
}

/**
 * 구 Mesh 데이터 작성 
 * @param radius 구의 반지름  
 * @param hDiv 수평 방향의 조각 수 
 * @param vDiv 높이 방향의 조각수 
 * @return mesh 데이터 
 */
function createSphereMesh( radius:Number, hDiv:uint, vDiv:uint ):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 * 2 * j / vDiv;
			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( i / hDiv, 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;
}