Saturn 3D, 토성, 고리 중첩 해결못한 것
토성 예제.
@author Yongho, Ji
@since 2009.07.06
@see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
/**
* Copyright jidolstar ( http://wonderfl.net/user/jidolstar )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/cL1u
*/
package
{
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.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.ui.Keyboard;
import flash.utils.getTimer;
[SWF(width=400, height=400, frameRate=32, backgroundColor=0x000000)]
/**
* 토성 예제.
* @author Yongho, Ji
* @since 2009.07.06
* @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WSF24A5A75-38D6-4a44-BDC6-927A2B123E90.html
*/
public class Saturn3D extends Sprite
{
// 투영된 토성 표면 Vertex 정보
private var saturnFaceProjected:Vector.<Number> = new Vector.<Number>(0, false);
// 투영된 토성 링부분 Vertex 정보
private var saturnRingProjected: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();
// 토성 표면 Mesh 데이타
private var saturnFaceMesh:GraphicsTrianglePath;
// 토성 고리 Mesh 데이타
private var saturnRingMesh:GraphicsTrianglePath;
// 토성 표면 텍스쳐
private var saturnFaceTexture:BitmapData;
// 토성 고리 텍스쳐
private var saturnRingTexture:BitmapData;
private var texture:BitmapData;
// Viewport의 Z축 위치
private var viewPortZAxis:Number = 200;
// triangle을 보여줄지 여부
private var visibleTriangle:Boolean = true;
/**
* 생성자
*/
public function Saturn3D()
{
super();
//Textures
saturnFaceTexture = new BitmapData( 400, 200, false );
saturnRingTexture = new BitmapData( 60, 30, false );
saturnFaceTexture.perlinNoise(64, 64, 3, 0, true, true, 7, true);
saturnRingTexture.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 = 60;
//Mesh 데이타
saturnFaceMesh = createSphereMesh( 50, 32, 32 );
saturnRingMesh = createRingMesh( 70, 20, 50 );
//이벤트 처리
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 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;
}
}
/**
* 프레임 마다 처리.
*/
private function onEnterFrame( event:Event ):void
{
world.identity(); //단위행렬로 전환
world.appendRotation( getTimer() * 0.027, 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, saturnFaceMesh.vertices, saturnFaceProjected, saturnFaceMesh.uvtData );
Utils3D.projectVectors( world, saturnRingMesh.vertices, saturnRingProjected, saturnRingMesh.uvtData );
viewport.graphics.clear();
// Triangle 라인을 그림
if( visibleTriangle )
{
viewport.graphics.lineStyle( 1, 0xff0000, 0.1 );
}
//Texture 입힌다.
viewport.graphics.beginBitmapFill( saturnFaceTexture, null, false, true );
viewport.graphics.drawTriangles( saturnFaceProjected, getSortedIndices(saturnFaceMesh), saturnFaceMesh.uvtData, saturnFaceMesh.culling );
viewport.graphics.beginBitmapFill( saturnRingTexture, null, false, true );
viewport.graphics.drawTriangles( saturnRingProjected, getSortedIndices(saturnRingMesh), saturnRingMesh.uvtData, saturnRingMesh.culling );
}
}
}
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 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 * 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( 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;
}
/**
* 고리 Mesh 데이터 작성
* @param radius 고리 안쪽 반지름
* @param thickness 고리 두께
* @param div 고리 조각수
* @return mesh 데이터
*/
function createRingMesh( radius:Number, thickness:Number, div: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 );
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(
0,1,1,
1,1,1,
0,0,1,
1,0,1
);
//Indices
n = i * 4;
mesh.indices.push( n, n+1, n+2, n+2, n+1, n+3 );
}
return mesh;
}