forked from: 3D Example, Flash Player 10 : flash on 2009-7-2
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
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;
}