package com.dafishinsea.models { import flash.display.Bitmap; import flash.display.BitmapData; import flash.geom.Matrix3D; import flash.geom.Vector3D; public class Heart implements IModel { private const PI:Number = Math.PI;//half revolution in radians private const HALFPI:Number = Math.PI/2;//1/4 revolution in radians private const TWOPI:Number = 2*Math.PI;//full revolution in radians private var _indices:Vector. = new Vector.(); private var _vertices:Vector. = new Vector.(); private var _uvts:Vector. = new Vector.(); private var _colorMap:BitmapData; private var _normalMap:BitmapData; private var _textureMap:BitmapData; private var _radius:Number; private var _rows:int; private var _cols:int; private var _mapWidth:Number; private var _mapHeight:Number; private var _matrix:Matrix3D = new Matrix3D(); private var _inverseMatrix:Matrix3D = new Matrix3D(); public const X_AXIS:Vector3D = new Vector3D(1, 0, 0); public const Y_AXIS:Vector3D = new Vector3D(0, 1, 0); public const Z_AXIS:Vector3D = new Vector3D(0, 0, 1); public function get vertices():Vector. { return _vertices; } public function get indices():Vector. { return _indices; } public function get uvts():Vector. { return _uvts; } public function get normalMap():BitmapData { return _normalMap; } /** * matrix */ public function get matrix():Matrix3D { return _matrix; } public function set matrix(newValue:Matrix3D):void { _matrix = newValue; } /** * colorMap - determines mapHeight and mapWidth */ public function get colorMap():BitmapData { return _colorMap; } public function set colorMap(newValue:BitmapData):void { _colorMap = newValue; _mapWidth = _colorMap.width; _mapHeight = _colorMap.height; } /** * textureMap - determines mapHeight & mapWidth */ public function get textureMap():BitmapData { return _textureMap; } public function set textureMap(newValue:BitmapData):void { _textureMap = newValue; } public function Heart(colorMap:BitmapData, radius:Number=100, rows:int=20, cols:int=20) { _radius = radius; _rows = rows; _cols = cols; _colorMap = colorMap; _mapWidth = _colorMap.width; _mapHeight = _colorMap.height; _textureMap = new BitmapData(_mapWidth, _mapHeight, false, 0xFF0000); init(); } private function init():void { createSphericalGeometry(); createNormalMap(); } /** * create geometry (vertices/indices) and texture coordinates (uvt) * this creates heart upside down -- need to invert */ private function createSphericalGeometry():void { var lon_incr:Number = TWOPI/_cols; var lat_incr:Number = PI/_rows; var lat:Number = 0;//angle of rotation around the x axis var lon:Number = 0;//angle of rotation around the y axis, *in radians* var x:Number, y:Number, z:Number; var vnum:int = 0; var ind:int = 0; for(var h:int = 0; h <= _rows; h++) { lon = 0; for(var v:int = 0; v <= _cols; v++) { var point:Vector3D = getSurfacePoint(lat, lon); //1. add vertex triplet _vertices[vnum] = point.x; _vertices[vnum+1] = point.y; _vertices[vnum+2] = point.z; //2. uvts _uvts[vnum] = v/_cols; _uvts[vnum+1] = h/_rows; _uvts[vnum+2] = 1; vnum+=3; //3. add indices if(h < _rows && v < _cols){ _indices.push(ind, ind+1, ind + _cols+1); _indices.push(ind + _cols+1, ind+1, ind + _cols + 2); } ind+=1; lon += lon_incr; } lat += lat_incr; } } private function getSurfacePoint(lat:Number, lon:Number):Vector3D { var x,y,z,pre_x,dist,fade,temp_radius,c,cy:Number; if(lat < HALFPI) { pre_x = _radius*Math.cos(lon)*Math.sin(lat); //increase the radius a bit when x approaches midpoint between 0 and radius or -radius dist = (_radius - Math.abs(pre_x))/_radius; fade = (HALFPI-lat)/HALFPI; temp_radius = _radius - fade*fade*dist*_radius; x = temp_radius*Math.cos(lon)*Math.sin(lat); y = (_radius - (fade*dist*dist*_radius))*Math.cos(lat); z = temp_radius*0.6*Math.sin(lon)*Math.sin(lat); } else { //taper to a point x = _radius*Math.cos(lon)*Math.sin(lat); //interpolate a linear and circular value - more linear towards end c = (lat-HALFPI)/HALFPI; c=c*c; cy = _radius*Math.cos(lat); y = cy*c*1.5 + (1-c)*cy; z = _radius*0.6*Math.sin(lon)*Math.sin(lat); } return new Vector3D(x,y,z); } /** * create normal map --evaluate normal for each pixel based lat/lon at that position in the sphere */ private function createNormalMap():void { //_normalMap = Bitmap(new HeartNormalMap()).bitmapData; var radius:Number = 1; var lon:Number = 0; var lat:Number = 0; var bot_lat:Number = 0; var right_lon:Number = 0; _normalMap = new BitmapData(_mapWidth, _mapHeight, false, 0xFFFFFF); for(var v:int = 0; v < _mapHeight; ++v) { lat = (v/_mapHeight)*PI; bot_lat = ((v+1)/_mapHeight)*PI; for(var u:int = 0; u < _mapWidth; ++u) { lon = (u/_mapWidth)*TWOPI; right_lon = ((u+1)/_mapWidth)*TWOPI; //center point var cp:Vector3D = getSurfacePoint(lat, lon); //right point var rp:Vector3D = getSurfacePoint(lat, right_lon); //bottom var bp:Vector3D = getSurfacePoint(bot_lat, lon); //get normal of triangle formed by three points var d1:Vector3D = cp.subtract(rp); var d2:Vector3D = cp.subtract(bp); var norm:Vector3D = d1.crossProduct(d2); norm.normalize(); var r:uint = 255*(norm.x + 1)/2; var g:uint = 255*(norm.y + 1)/2; var b:uint = 255*(norm.z + 1)/2; var normalColor:uint = r << 16 | g << 8 | b; _normalMap.setPixel(u,v,normalColor); } } } } }