package com.dafishinsea.demos { import flash.display.Sprite; import flash.display.Shape; import flash.events.Event; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.TriangleCulling; import flash.filters.BlurFilter; import flash.geom.PerspectiveProjection; import flash.geom.Utils3D; import flash.geom.Rectangle; import flash.geom.Point; import flash.geom.Vector3D; import flash.geom.Matrix3D; import flash.display.StageAlign; import flash.display.StageScaleMode; [SWF(backgroundColor="0x000000", width="800", height="600", frameRate="30")] public class Heart extends Sprite { private var texture:BitmapData = new BitmapData(500,500,false,0xFF0000); private var vertices:Vector.; private var abs_vertices:Vector.; private var indices:Vector.; private var uvtData:Vector.; private var perspective: PerspectiveProjection; private var projectionMatrix : Matrix3D; private var projectedVerts:Vector.; private var focalLength:Number = 50; private var container:Sprite; //x,yz rotation private var rx:Number = 60; private var ry:Number = 40; private var rz:Number = 30; private var testBmp:Bitmap; public function Heart() { init(); } private function init():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; vertices = new Vector.(); indices = new Vector.(); uvtData = new Vector.(); //set up perspective perspective = new PerspectiveProjection(); perspective.fieldOfView = 50; //3D transformation matrix - used to rotate object projectionMatrix = perspective.toMatrix3D(); projectedVerts = new Vector.(); //container to hold scene container = new Sprite(); container.x = stage.stageWidth/2; container.y = stage.stageHeight/3; container.z = 50; container.rotationX = 180; addChild(container); //add test bitmap testBmp = new Bitmap(texture); //container.addChild(testBmp); createSphere(200, 5,5); addEventListener(Event.ENTER_FRAME, onEnterFrame); //render(); } private function createSphere(radius:Number, latspacing:int, longspacing:int):void { //sphere for(var lat:int = 0; lat <= 180; lat += latspacing){ for(var long:int = 0; long<= 360; long += longspacing){ //get x,y,z for the point at lat, long //x = r*cos(long)*sin(lat) //y = r*cos(lat) //z = r*sin(long)*sin(lat) if(lat < 90) { //circular interpolation var pre_x:Number = radius*Math.cos(long*Math.PI/180)*Math.sin(lat*Math.PI/180); //increase the radius a bit when x approaches midpoint between 0 and radius or -radius var dist:Number = (radius - Math.abs(pre_x))/radius; var fade:Number = (90-lat)/90; var temp_radius:Number = radius - fade*fade*dist*radius; var _x:Number = temp_radius*Math.cos(long*Math.PI/180)*Math.sin(lat*Math.PI/180); var _y:Number = (radius - (fade*dist*dist*radius))*Math.cos(lat*Math.PI/180); var _z:Number = temp_radius*0.6*Math.sin(long*Math.PI/180)*Math.sin(lat*Math.PI/180); } else { //taper to a point var _x:Number = radius*Math.cos(long*Math.PI/180)*Math.sin(lat*Math.PI/180); //interpolate a linear and circular value - more linear towards end var c:Number = (lat-90)/90; c=c*c; var cy:Number = radius*Math.cos(lat*Math.PI/180); var _y:Number = cy*c*1.5 + (1-c)*cy; var _z:Number = radius*0.6*Math.sin(long*Math.PI/180)*Math.sin(lat*Math.PI/180); } vertices.push(_x,_y,_z); uvtData.push(long/360, lat/180, 1); } } //uvtData trace("uvt:"+uvtData); //OK, now for triangles var rows:Number = 180/latspacing; var cols:Number = 360/longspacing +1; /* triangle arrangement i--------(i+1) | / | | / | | / | |/ | i2 ------(i2 +1) */ for(var row:int = 0; row < rows; row++){ for(var col:int = 0; col < cols; col++){ var i:int = row*(cols) + col; var i2:int = (row+1)*(cols) + col; if(col+1 != cols) { indices.push(i, i + 1, i2); indices.push(i2, i + 1, i2 + 1); } //} else { //last rectangle - back to start //indices.push(i, i - cols + 1, i2); //indices.push(i2, i - cols + 1, i2 - cols + 1); //} } } } private function onEnterFrame(event:Event):void { render(); } private function render():void { container.rotationY += 1; projectionMatrix = container.transform.matrix3D; //determine the absolute position of the vertices abs_vertices = new Vector.(); projectionMatrix.transformVectors(vertices, abs_vertices); //var abs_vertices = vertices; //update texture map based on triangle normals var tr:Shape = new Shape(); //for(var i:int = 0; i < indices.length; i+=3){ for(var i:int = 0; i < indices.length; i+=3){ //get the 3 points of the triangle as Vector3D objects var pt1:Vector3D = new Vector3D(abs_vertices[indices[i]*3], abs_vertices[indices[i]*3+1], abs_vertices[indices[i]*3+2]); var pt2:Vector3D = new Vector3D(abs_vertices[indices[i+1]*3], abs_vertices[indices[i+1]*3+1], abs_vertices[indices[i+1]*3+2]); var pt3:Vector3D = new Vector3D(abs_vertices[indices[i+2]*3], abs_vertices[indices[i+2]*3+1], abs_vertices[indices[i+2]*3+2]); //subtract the first two from the third var d1:Vector3D = pt3.subtract(pt1); var d2:Vector3D = pt3.subtract(pt2); //get the cross-product of the results to get the normal var normal:Vector3D = d1.crossProduct(d2); //trace("normal"+normal); //get the angle between the normal and the lighting angle var lightDir:Vector3D = new Vector3D(100,100,100); var angleDiff:Number = Vector3D.angleBetween(lightDir,normal); //normalize shade to 0-1 var a:Number = isNaN(angleDiff)?1:(angleDiff)/(Math.PI); var r:int = a*255; var shadow:uint = r << 16 | r << 8 | r; //creates a disco effect ... //var shadow:uint = a*0xFFFFFF; //draw shaded texture //tr.graphics.lineStyle(1,0x00FF00); tr.graphics.beginFill(shadow); //trace(uvtData[indices[i]*3]+","+uvtData[indices[i]*3+1]); var p1:Point = new Point(uvtData[indices[i]*3]*texture.width, uvtData[indices[i]*3+1]*texture.height); var p2:Point = new Point(uvtData[indices[i+1]*3]*texture.width, uvtData[indices[i+1]*3+1]*texture.height); var p3:Point = new Point(uvtData[indices[i+2]*3]*texture.width, uvtData[indices[i+2]*3+1]*texture.height); /*trace("("+p1.x+","+p1.y+")"); trace("("+p2.x+","+p2.y+")"); trace("("+p3.x+","+p3.y+")");*/ tr.graphics.moveTo(p1.x, p1.y); tr.graphics.lineTo(p2.x, p2.y); tr.graphics.lineTo(p3.x, p3.y); tr.graphics.lineTo(p1.x, p1.y); tr.graphics.endFill(); } var lighting:BitmapData = new BitmapData(texture.width, texture.height,false,0x9F0000); lighting.draw(tr); //blur lighting.applyFilter(lighting, new Rectangle(0,0, texture.width, texture.height), new Point(0,0),new BlurFilter(5,5,3)); texture =new BitmapData(500,500,false,0xFF0000); texture.merge(lighting, new Rectangle(0,0, texture.width, texture.height),new Point(0,0),0xFF,0x00,0x00,0x80); //addChild(tr); graphics.clear(); Utils3D.projectVectors(projectionMatrix, vertices, projectedVerts, uvtData); graphics.beginBitmapFill(texture,null, false, false); //graphics.beginBitmapFill(lighting,null, false, false); graphics.drawTriangles(projectedVerts, indices, uvtData, TriangleCulling.POSITIVE); graphics.endFill(); } } }