octo2sphere
catmull-clark transformation
/* catmull-clark transformation */
package {
import flash.display.*;
import flash.geom.*;
import flash.filters.*;
import flash.events.*;
import flash.utils.*;
import flash.text.*;
[SWF(width="400", height="400", backgroundColor="0xffffff", frameRate="8")]
/**
* @author : Mateusz Malczak (http://segfaultlabs.com)
*/
public class sphere extends MovieClip {
private var m3d : Matrix3D;
private var s : BitmapData;
private var R :Number;
private var t :Timer;
private var tf : TextField;
private var vs:Vector.<Number> = new Vector.<Number>(0,false);
private var vsp:Vector.<Number> = new Vector.<Number>(0,false);
private var fs:Vector.<int> = new Vector.<int>(0,false);
private var uvt:Vector.<Number> = new Vector.<Number>(0,false);
public function sphere():void
{
s = new BitmapData(320,320);
var tmp0:BitmapData = new BitmapData(320,320);
s.perlinNoise(320,320,8,Math.random(),true,true,BitmapDataChannel.BLUE,false);
tmp0.perlinNoise(320,320,8,Math.random(),true,true,BitmapDataChannel.GREEN,false);
s.draw( tmp0, null, null, BlendMode.ADD );
tmp0.perlinNoise(320,320,8,Math.random(),true,true,BitmapDataChannel.BLUE,false);
s.draw( tmp0, null, null, BlendMode.ADD );
tmp0.perlinNoise(320,320,8,Math.random(),true,true,BitmapDataChannel.ALPHA,false);
s.draw( tmp0, null, null, BlendMode.ADD );
tmp0.dispose();
R = 160; /* sphere radius */
var pi4:Number = Math.PI*0.25;
var pi2:Number = 90 * Math.PI / 180;
/* octohedra vertices */
vs.push( 0, -R, 0 );
vs.push( R * Math.sin(pi4),0, R* Math.cos(pi4) );
pi4 += pi2;
vs.push( R * Math.sin(pi4),0, R* Math.cos(pi4) );
pi4 += pi2;
vs.push( R * Math.sin(pi4),0, R* Math.cos(pi4) );
pi4 += pi2;
vs.push( R * Math.sin(pi4),0, R* Math.cos(pi4) );
pi4 = Math.PI*0.25;
/* one vertex extra to get proper bitmap textring */
vs.push( R * Math.sin(pi4),0, R* Math.cos(pi4) );
vs.push( 0, R, 0 );
/* faces */
fs.push( 0,1,2 );
fs.push( 0,2,3 );
fs.push( 0,3,4 );
fs.push( 0,4,5 );
fs.push( 0,1,5 );
fs.push( 6,2,1 );
fs.push( 6,3,2 );
fs.push( 6,4,3 );
fs.push( 6,5,4 );
fs.push( 6,1,5 );
/* texturing */
uvt.push( 0.5 ,0 , 0 );
uvt.push( 0 ,0.5, 0 );
uvt.push( 0.25,0.5, 0 );
uvt.push( 0.50,0.5, 0 );
uvt.push( 0.75,0.5, 0 );
uvt.push( 1 ,0.5, 0 );
uvt.push( 0.5 ,1 , 0 );
vsp.length = vs.length/3*2;
m3d = new Matrix3D();
m3d.appendRotation(30,Vector3D.Y_AXIS );
m3d.appendRotation(30,Vector3D.X_AXIS );
m3d.appendTranslation( 200, 200, 300 );
var pp:PerspectiveProjection = transform.perspectiveProjection;
pp.fieldOfView = 60;
pp.focalLength = 200;
pp.projectionCenter = new Point(200,200);
transform.perspectiveProjection = pp;
addEventListener( Event.ENTER_FRAME, ef );
t = new Timer(5000,5);
t.addEventListener( TimerEvent.TIMER, subdivide );
t.start();
draw();
tf = new TextField();
tf.autoSize = "left";
tf.text = "octahedra transformed into sphere\nvertices : "+vs.length/3;
addChild(tf);
}
/**
* catmull-clark division, without duplicating edge division
*/
public function subdivide( evt:Event ):void
{
var cnt:int = fs.length;
var i:int, j:int, k:int, n:int;
var i3:int, j3:int, k3:int;
var n1:int, n2:int, n3:int;
var v:Vector3D = new Vector3D();
var done:Object = {};
n = vs.length/3;
var e:String = "";
while ( cnt>0 )
{
i = fs.shift(); j = fs.shift(); k = fs.shift();
i3 = i*3; j3 = j*3; k3 = k*3;
e = Math.max(i,j)+""+Math.min(i,j);
if ( done[e]==null )
{
v.x = 0.5*(vs[i3 ] + vs[j3 ]);
v.y = 0.5*(vs[i3+1] + vs[j3+1]);
v.z = 0.5*(vs[i3+2] + vs[j3+2]);
v.scaleBy(R/v.length);
vs.push(v.x,v.y,v.z);
uvt.push( (uvt[i3]+uvt[j3])/2, (uvt[i3+1]+uvt[j3+1])/2, 0 );
done[e] = n1 = n;
n+=1;
} else n1 = done[e];
e = Math.max(j,k)+""+Math.min(j,k);
if ( done[e]==null )
{
v.x = 0.5*(vs[j3] + vs[k3]);
v.y = 0.5*(vs[j3+1] + vs[k3+1]);
v.z = 0.5*(vs[j3+2]+vs[k3+2]);
v.scaleBy(R/v.length);
vs.push(v.x,v.y,v.z);
uvt.push( (uvt[j3]+uvt[k3])/2, (uvt[j3+1]+uvt[k3+1])/2, 0 );
done[e] = n2 = n;
n+=1
} else n2 = done[e];
e = Math.max(k,i)+""+Math.min(k,i);
if ( done[e]==null )
{
v.x = 0.5*(vs[k3] + vs[i3]);
v.y = 0.5*(vs[k3+1] + vs[i3+1]);
v.z = 0.5*(vs[k3+2]+vs[i3+2]);
v.scaleBy(R/v.length);
vs.push(v.x,v.y,v.z);
uvt.push( (uvt[k3]+uvt[i3])/2, (uvt[k3+1]+uvt[i3+1])/2, 0 );
done[e] = n3 = n;
n+=1;
} else n3 = done[e];
fs.push( i, n1, n3 );
fs.push( n1, j, n2 );
fs.push( n3, n2, k );
fs.push( n3, n1, n2 );
cnt -= 3;
};
done = null;
vsp.length = vs.length/3*2;
tf.text = tf.text + "\nvertices : "+n;
}
public function draw():void
{
graphics.clear();
Utils3D.projectVectors( m3d, vs, vsp, uvt );
graphics.lineStyle(1,0x000000,0.6,true,LineScaleMode.NONE);
graphics.beginBitmapFill(s,null,false,true);
graphics.drawTriangles( vsp, fs, uvt, "positive" );
};
public function ef( evt:Event ):void
{
m3d.prependRotation( 15, Vector3D.Y_AXIS );
m3d.prependRotation( 2, Vector3D.X_AXIS );
draw();
}
}
}