/* * Copyright 2007 (c) Gabriel Putnam * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package away3d.primitives { import away3d.arcane; import away3d.core.base.*; use namespace arcane; /** * Creates a 3d geodesic sphere primitive. */ public class GeodesicSphere extends AbstractPrimitive { private var _radius:Number; private var _fractures:Number; private function buildGeodesicSphere( radius_in:Number, fractures_in:int ):void { // Set up variables for keeping track of the vertices, faces, and texture coords. var aVertice:Array = []; var aUV:Array = []; // Set up variables for keeping track of the number of iterations and the angles var iVerts:uint = fractures_in + 1, jVerts:uint; var j:uint, Theta:Number=0, Phi:Number=0, ThetaDel:Number, PhiDel:Number; var cosTheta:Number, sinTheta:Number, rcosPhi:Number, rsinPhi:Number; // Set up variables for figuring out the texture coordinates using a diamond ~equal area map projection // This is done so that there is the minimal amount of distortion of textures around poles. // Visually, this map projection looks like this. /* Phi /\0,0 | / \ \/ / \ / \ / \ / 1,0 \0,1 \ Theta-> / \ / \ / \ / \ / \/1,1 */ var Pd4:Number = Math.PI / 4, cosPd4:Number = Math.cos(Pd4), sinPd4:Number = Math.sin(Pd4), PIInv:Number = 1/Math.PI; var R_00:Number = cosPd4, R_01:Number = -sinPd4, R_10:Number = sinPd4, R_11:Number = cosPd4; var Scale:Number = Math.SQRT2, uOff:Number = 0.5, vOff:Number = 0.5; var oVtx:Vertex, UU:Number, VV:Number, u:Number, v:Number; PhiDel = Math.PI / ( 2 * iVerts); // Build the top vertex aVertice.push( createVertex( 0, 0, radius_in ) ); //i++; Phi += PhiDel; // Build the tops worth of vertices for the sphere progressing in rings around the sphere var i:int; for( i = 1 ; i <= iVerts; ++i){ j = 0; jVerts = i*4; Theta = 0; ThetaDel = 2* Math.PI / jVerts; rcosPhi = Math.cos( Phi ) * radius_in; rsinPhi = Math.sin( Phi ) * radius_in; for( j; j < jVerts; ++j ){ UU = Theta * PIInv - 0.5; VV = ( Phi * PIInv - 1 ) * ( 0.5 - Math.abs( UU ) ); u = ( UU * R_00 + VV * R_01 ) * Scale + uOff; v = ( UU * R_10 + VV * R_11 ) * Scale + vOff; cosTheta = Math.cos( Theta ); sinTheta = Math.sin( Theta ); aVertice.push( createVertex( cosTheta * rsinPhi, sinTheta * rsinPhi, rcosPhi ) ); aUV.push(createUV( u, v ) ); Theta += ThetaDel; } Phi += PhiDel; } // Build the bottom worth of vertices for the sphere. i = iVerts-1; for( i; i >0; i-- ){ j = 0; jVerts = i*4; Theta = 0; ThetaDel = 2* Math.PI / jVerts; rcosPhi = Math.cos( Phi ) * radius_in; rsinPhi = Math.sin( Phi ) * radius_in; for( j; j < jVerts; ++j ){ cosTheta = Math.cos( Theta ); sinTheta = Math.sin( Theta ); oVtx = createVertex( cosTheta * rsinPhi, sinTheta * rsinPhi, rcosPhi ); aVertice.push( oVtx ); Theta += ThetaDel; } Phi += PhiDel; } // Build the last vertice aVertice.push( createVertex( 0, 0, -radius_in ) ); // Build the faces for the sphere // Build the upper four sections var k:uint, L_Ind_s:uint, U_Ind_s:uint, U_Ind_e:uint, L_Ind_e:uint, L_Ind:uint, U_Ind:uint; var isUpTri:Boolean, Pt0:uint, Pt1:uint, Pt2:uint, triInd:uint, tris:uint; tris = 1; var v0:Vertex; var v1:Vertex; var v2:Vertex; var uv0:Vertex; var uv1:Vertex; L_Ind_s = 0; L_Ind_e = 0; for( i = 0; i < iVerts; i++ ){ U_Ind_s = L_Ind_s; U_Ind_e = L_Ind_e; if( i == 0 ) L_Ind_s++; L_Ind_s += 4*i; L_Ind_e += 4*(i+1); U_Ind = U_Ind_s; L_Ind = L_Ind_s; for( k = 0; k < 4; ++k ){ isUpTri = true; for( triInd = 0; triInd < tris; triInd++ ){ if( isUpTri ){ Pt0 = U_Ind; Pt1 = L_Ind; L_Ind++; if( L_Ind > L_Ind_e ) L_Ind = L_Ind_s; Pt2 = L_Ind; isUpTri = false; } else { Pt0 = L_Ind; Pt2 = U_Ind; U_Ind++; if( U_Ind > U_Ind_e ) U_Ind = U_Ind_s; Pt1 = U_Ind; isUpTri = true; } addFace( createFace( aVertice[Pt1],aVertice[Pt0], aVertice[Pt2] , null, aUV[Pt1],aUV[Pt0],aUV[Pt2] ) ); } } tris += 2; } U_Ind_s = L_Ind_s; U_Ind_e = L_Ind_e; // Build the lower four sections for( i = iVerts-1; i >= 0; i-- ){ L_Ind_s = U_Ind_s; L_Ind_e = U_Ind_e; U_Ind_s = L_Ind_s + 4*(i+1); U_Ind_e = L_Ind_e + 4*i; if( i == 0 ) U_Ind_e++; tris -= 2; U_Ind = U_Ind_s; L_Ind = L_Ind_s; for( k = 0; k < 4; ++k ){ isUpTri = true; for( triInd = 0; triInd < tris; triInd++ ){ if( isUpTri ){ Pt0 = U_Ind; Pt1 = L_Ind; L_Ind++; if( L_Ind > L_Ind_e ) L_Ind = L_Ind_s; Pt2 = L_Ind; isUpTri = false; } else { Pt0 = L_Ind; Pt2 = U_Ind; U_Ind++; if( U_Ind > U_Ind_e ) U_Ind = U_Ind_s; Pt1 = U_Ind; isUpTri = true; } addFace( createFace( aVertice[Pt2],aVertice[Pt0],aVertice[Pt1] , null, aUV[Pt2],aUV[Pt0],aUV[Pt1] ) ); } } } } /** * Defines the radius of the sphere. Defaults to 100. */ public function get radius():Number { return _radius; } public function set radius(val:Number):void { if (_radius == val) return; _radius = val; _primitiveDirty = true; } /** * Defines the fractures of the sphere. Defaults to 2. */ public function get fractures():Number { return _fractures; } public function set fractures(val:Number):void { if (_fractures == val) return; _fractures = val; _primitiveDirty = true; } /** * Creates a new GeodesicSphere object. * * @param init [optional] An initialisation object for specifying default instance properties. */ public function GeodesicSphere( init:Object = null) { super(init); _radius = ini.getNumber("radius", 100, {min:100}); _fractures = ini.getInt("fractures", 2, {min:2}); buildGeodesicSphere(_radius, _fractures); type = "GeoSphere"; url = "primitive"; } /** * @inheritDoc */ public override function buildPrimitive():void { super.buildPrimitive(); buildGeodesicSphere(_radius, _fractures); } } }