[3D]GeodesicSphere for Papervision3D
Papervison3d GeodesicSphere 테스트
*
* http://blog.jidolstar.com/643
*
* @author Yongho, Ji
* @see texture http://visibleearth.nasa.gov/
/**
* Copyright jidolstar ( http://wonderfl.net/user/jidolstar )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/wX0O
*/
/**
* Papervison3d GeodesicSphere 테스트
*
* http://blog.jidolstar.com/643
*
* @author Yongho, Ji
* @see texture http://visibleearth.nasa.gov/
*/
package {
import com.bit101.components.*;
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.*;
import flash.ui.*;
import net.hires.debug.Stats;
[SWF(width=800, height=800, backgroundColor="#000000")]
public class test_pv3d extends Sprite {
private var _view:SphereView;
private var _radius:Number=300;
private var _fractures:Number=5;
private var _showFrame:Boolean=true;
public function test_pv3d() {
super();
stage.align=StageAlign.TOP_LEFT;
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.frameRate=60;
createSphereView();
stage.addChild(new Stats());
}
private function createSphereView():void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
var texture:BitmapData = Bitmap(loader.contentLoaderInfo.content).bitmapData;
_view=new SphereView(stage.stageWidth, stage.stageHeight, _radius, _fractures, _showFrame, texture);
stage.addChild(_view);
createSphereViewController();
});
loader.load(new URLRequest("http://lab.alumican.net/wonderfl/fp10sphere/texture.png"), new LoaderContext(true));
}
private function createSphereViewController():void {
var vertexCountLabel:Label=new Label(stage, 100, 86, "");
var surfaceCountLabel:Label=new Label(stage, 180, 86, "");
function updateCount():void {
vertexCountLabel.text="vertex : " + String(_view.getVertexCount());
}
new Label(stage, 100, 10, "SHAPE");
var fractureSlider:HUISlider=new HUISlider(stage, 100, 26, "fractures", function(e:Event):void {
var fracture:Number=Math.round(e.target.value);
if (fracture == _fractures)
return;
_fractures=fracture;
_view.createSphere(_radius, _fractures, _showFrame);
updateCount();
});
var radiusSlider:HUISlider=new HUISlider(stage, 100, 46, "Radius", function(e:Event):void {
var radius:Number=Math.round(e.target.value);
if (radius == _radius)
return;
_radius=radius;
_view.createSphere(_radius, _fractures, _showFrame);
updateCount();
});
var viewFrameCheckBox:CheckBox=new CheckBox(stage, 100, 66, "Show Wire Frame", function(e:Event):void {
_showFrame=viewFrameCheckBox.selected;
_view.createSphere(_radius, _fractures, _showFrame);
});
viewFrameCheckBox.selected=_showFrame;
radiusSlider.minimum=10;
radiusSlider.maximum=800;
radiusSlider.labelPrecision=0;
radiusSlider.value=_radius;
fractureSlider.minimum=0;
fractureSlider.maximum=30;
fractureSlider.labelPrecision=0;
fractureSlider.value=_fractures;
updateCount();
}
}
}
import flash.display.*;
import flash.events.*;
import flash.net.URLRequest;
import flash.utils.*;
import org.papervision3d.Papervision3D;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.NumberUV;
import org.papervision3d.core.proto.MaterialObject3D;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.materials.WireframeMaterial;
import org.papervision3d.view.BasicView;
/**
* Sphere를 보여주는 화면
*/
class SphereView extends BasicView {
//[Embed(source="../assets/earthmap1k.jpg")]
//private var SKY:Class;
private var _sphere:GeodesicSphere;
private var _frameSphere:GeodesicSphere;
private var _material:MaterialObject3D;
private var _vx:Number=0;
private var _vy:Number=0;
public function SphereView($viewportWidth:Number, $viewportHeight:Number, $radius:Number, $fractures:Number, $viewframe:Boolean, $texture:BitmapData) {
super($viewportWidth, $viewportHeight);
_material=new BitmapMaterial($texture) as MaterialObject3D;
_material.smooth=true;
_material.doubleSided=false;
createSphere($radius, $fractures, $viewframe);
startRendering();
}
public function createSphere($radius:Number, $fractures:Number, $showFrame:Boolean):void {
if (_sphere)
scene.removeChild(_sphere);
if (_frameSphere)
scene.removeChild(_frameSphere);
_sphere=new GeodesicSphere(_material, $radius, $fractures);
scene.addChild(_sphere);
if ($showFrame) {
_frameSphere=new GeodesicSphere(new WireframeMaterial(0xffffff, 0.2, 1), $radius + 1, $fractures);
scene.addChild(_frameSphere);
}
}
override protected function onRenderTick($event:Event=null):void {
_vx+=(-(mouseY / stage.stageHeight - 0.5) * 5 - _vx) * 0.3;
_vy+=((mouseX / stage.stageWidth - 0.5) * 5 - _vy) * 0.3;
//http://blog.federicocalvo.com/2009/03/papervision-3d-sphere-globla-axis.html
var tempMatrix:Matrix3D=_sphere.transform;
var rotX:Matrix3D=Matrix3D.rotationX(_vx * Math.PI / 180);
var rotY:Matrix3D=Matrix3D.rotationY(_vy * Math.PI / 180);
var rotResult:Matrix3D=Matrix3D.multiply(rotX, rotY);
_sphere.transform=Matrix3D.multiply(rotResult, tempMatrix);
if (_frameSphere) {
_frameSphere.transform=_sphere.transform;
}
super.onRenderTick($event);
}
public function getVertexCount():int {
return _sphere.geometry.vertices.length;
}
}
/**
* GeodesicSphere
*/
class GeodesicSphere extends TriangleMesh3D {
static public var DEFAULT_RADIUS:Number=100;
static public var DEFAULT_SCALE:Number=1;
static public var MIN_FRACTURES:Number=0;
public function GeodesicSphere($material:MaterialObject3D, $radius:Number=100, $fractures:int=8) {
super($material, [], [], null);
$fractures=Math.max(MIN_FRACTURES, $fractures);
if ($radius == 0)
$radius=DEFAULT_RADIUS;
buildSphere($radius, $fractures);
}
private function buildSphere($radius:Number, $fractures:Number):void {
var vertices:Array=geometry.vertices;
var faces:Array=geometry.faces;
var uvtData:Array=[];
var D2R:Number=Math.PI / 180; //Degree->Radian
var R2D:Number=180 / Math.PI; //Radian->Degree
var TPI:Number=Math.PI * 2;
var PI:Number=Math.PI;
var HPI:Number=Math.PI / 2;
var hnLat:int=$fractures + 1; //위도 방향 쪼갠수/2
var nLat:int=2 * hnLat; //위도 방향 쪼갠수
var nLon:int; //위도에 대한 경도 방향 쪼갠수
var lon:Number; //경도 (단위:라디안)
var lat:Number; //위도(단위:라디안)
var dLat:Number=180 / nLat * D2R; //위도 간격(단위:라디안)
var dLon:Number; //경도 간격(단위:라디안)
var i:int;
var j:int;
var x:Number;
var y:Number;
var z:Number;
var sinLat:Number;
var cosLat:Number;
var sinLon:Number;
var cosLon:Number;
var u:Number;
var v:Number;
////////////////////////////////
// Vertex, UVT 데이타 만들기
////////////////////////////////
// latitude -90->0 :
vertices.push(new Vertex3D(0, 0, -$radius));
uvtData.push(new NumberUV(0, 0));
for (i=0; i < hnLat; i++) {
nLon=4 * (i + 1); //경도방향 꼭지점수 4, 8, 12, 16, 20...
dLon=360 / nLon * D2R;
lat=-HPI + (i + 1) * dLat;
v=(HPI + lat) / PI;
sinLat=Math.sin(lat);
cosLat=Math.cos(lat);
z=$radius * sinLat;
for (j=0; j <= nLon; j++) {
lon=j * dLon;
sinLon=Math.sin(lon);
cosLon=Math.cos(lon);
x=$radius * cosLat * cosLon;
y=$radius * cosLat * sinLon;
u=lon / TPI;
vertices.push(new Vertex3D(x, y, z));
uvtData.push(new NumberUV(u, v));
}
}
//latitude 0 -> 90
for (i=1; i < hnLat; i++) {
nLon=4 * (hnLat - i);
dLon=360 / nLon * D2R;
lat=dLat * i;
v=(HPI + lat) / PI;
sinLat=Math.sin(lat);
cosLat=Math.cos(lat);
z=$radius * sinLat;
for (j=0; j <= nLon; j++) {
lon=j * dLon;
sinLon=Math.sin(lon);
cosLon=Math.cos(lon);
x=$radius * cosLat * cosLon;
y=$radius * cosLat * sinLon;
u=lon / TPI;
vertices.push(new Vertex3D(x, y, z));
uvtData.push(new NumberUV(u, v));
}
}
vertices.push(new Vertex3D(0, 0, $radius));
uvtData.push(new NumberUV(1, 1));
////////////////////////////////
// Face 만들기
////////////////////////////////
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를 증가하기 위해 사용
//Latitude -90->0
tris=1;
u_idx_start=0;
u_idx_end=0;
for (i=0; i < hnLat; ++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;
}
faces.push(new Triangle3D(this, [vertices[pt0], vertices[pt2], vertices[pt1]], material, [uvtData[pt0], uvtData[pt2], uvtData[pt1]]));
}
}
tris+=2; //한개의 분면에서 해당 적위에 대한 면의 수는 2씩 증가한다.
}
//Latitude 0 -> 90
for (i=hnLat - 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;
}
faces.push(new Triangle3D(this, [vertices[pt0], vertices[pt2], vertices[pt1]], material, [uvtData[pt0], uvtData[pt2], uvtData[pt1]]));
}
}
}
this.geometry.ready=true;
if (Papervision3D.useRIGHTHANDED)
this.geometry.flipFaces();
}
}