/* --------------- // Image Extruder // --------------- // // AUTHOR: Sammy Joe Osborne // DATE: 01/08/2010 // // DESCRIPTION: The image extruder evaluates the brightness of pixels in an image. Based on a pixels brightness, a plane is drawn in // 3D space at varying size and z-depth. This is similar in functionality of a bump-map in most 3D software. Color can be used, // amplitude can be adjusted, and the 3D view can be rotated all in real-time. // Thanks to Pickle from www.allflashwebsite.com for the autoOrbit classes. */ package { import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.display.Sprite; import flash.events.Event; import flash.display.Bitmap; import flash.display.BitmapData; import org.papervision3d.core.math.Matrix3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.view.BasicView; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.materials.special.LineMaterial; import org.papervision3d.core.geom.Lines3D; import org.papervision3d.core.geom.renderables.Line3D; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.Papervision3D; import org.papervision3d.objects.primitives.Sphere; //import com.afw.papervision3d.view.AutoOrbitDrag; //[SWF(width="640", height="480", backgroundColor="#ffffff", frameRate="60")] public class StuffYerFace extends BasicView { private static const FORWARD:Number3D = new Number3D(0, 0, 1); //for rotation private var previousMousePoint:Point = new Point(); //for rotation private var isMouseDown:Boolean = false; //for rotation /*[Embed(source="images/test2.jpg")] private var test2:Class;*/ //Set up variables const NUM_PLANES = 3000; //number of planes to compose the image of private var bmp:Bitmap; //the image that will be used private var maxSize:Number = 17; //the maximum size of each plane private var minSize:Number = 12; //the minimum size of each plane private var spacing:Number = 1; //spacing between the planes private var scale:int = 15; //how much larger than the actual image the plane construct should be private var _amplitude:Number = .5; //how severely brightness effects zDepth private var _colorize:Boolean = false;; //whether to use color or not private var numPlanesWide:int; //number of planes wide to construct it private var numPlanesHigh:int; //number of planes high to construct it private var containerWidth:Number; //how wide the whole plane container is private var containerHeight:Number; //how tall the whole plane container is private var camTarget:DisplayObject3D = new DisplayObject3D(null,null); //the target for the camera private var planeContainer:DisplayObject3D = new DisplayObject3D(); //groups all the planes together into one display object private var pcContainer:DisplayObject3D = new DisplayObject3D(); //another wrapper around the planeContainer (thus pcContainer) //private var orbitor:AutoOrbitDrag; private var sphere:Sphere; //var orbitInitialized:Boolean = false; private var planeArray:Array = new Array(); private var brightnessArray:Array = new Array(); private var pixelArray:Array = new Array(); var planeContainerAdded:Boolean = false; public function StuffYerFace() { Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger); //turns off debug trace statements //importBitmap(); //imports the bitmap and sets all size related variables based on the bitmap's dimensions trace(camera.x + ", " + camera.y + ", " + camera.z); addEventListener(MouseEvent.MOUSE_DOWN, stage_mouseDownHandler); //TODO change from stage to the view holder addEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler); //TODO see above addAxis(); //createPlanes(NUM_PLANES); sphere = new Sphere(); sphere.visible = true; //scene.addChild(sphere); startRendering(); } /*public function initOrbit():void { orbitor = new AutoOrbitDrag(this, sphere); orbitInitialized = true; }*/ public function importBitmap(bmpImport:Bitmap):void { bmp = bmpImport; var ratio:Number;//width to height ratio for the image so we can size the planes array accordingly //bmp = new Bitmap(new test2(1,1)); //imports the image to use ratio = bmp.height / bmp.width; //width to height ratio for the image so we can size the planes array accordingly numPlanesWide = Math.ceil(Math.sqrt(NUM_PLANES/ratio)); numPlanesHigh = Math.ceil(numPlanesWide*ratio); //x*scale + maxSize + spacing; containerWidth = (numPlanesWide*scale + maxSize + maxSize)* spacing; //fuck. there is no width or height property so containerHeight = (numPlanesHigh*scale + maxSize + maxSize)* spacing; //we gotta figure it out...how shitty is that. wtf. trace("containerWidth: " + containerWidth); trace("containerHeight: " + containerHeight); planeContainer.x = -containerWidth/2; planeContainer.y = containerHeight/2; if(!planeContainerAdded) { planeContainer.scaleX = -1; pcContainer.addChild(planeContainer); scene.addChild(pcContainer); planeContainerAdded = true; } //remove anything in the container if anything exists if(planeContainer.numChildren > 0) { removeChildren(planeContainer); } createPlanes(NUM_PLANES); } public function createPlanes(numPlanes:uint) { trace("w: " + numPlanesWide + "h: " + numPlanesHigh); var colorMaterial:ColorMaterial = new ColorMaterial(0x000000); //just a plane black material for now colorMaterial.doubleSided = true; var sizeRatio:Number = (bmp.width*bmp.height)/NUM_PLANES; var zDepth:Number; var pixelX, pixelY:int; var creationSize:Number; var newColorMaterial:ColorMaterial; for(var x:uint = 0; x<=numPlanesWide; x++) { planeArray[x] = new Array(); brightnessArray[x] = new Array(); pixelArray[x] = new Array(); for (var y:uint = 0; y<numPlanesHigh; y++) { pixelX = int(x*(bmp.width/numPlanesWide)); pixelY = int(y*(bmp.height/numPlanesHigh)); //trace("plane ("+x+","+" "+y+") is based on pixel " + "("+pixelX+", "+pixelY+")"); //pixel = bmp.bitmapData.getPixel(pixelX, pixelY); pixelArray[x][y] = bmp.bitmapData.getPixel(pixelX, pixelY); brightnessArray[x][y] = (calcBrightness(pixelArray[x][y])); creationSize = maxSize-(((brightnessArray[x][y]/255)*(maxSize-1))+minSize); trace(creationSize); if(creationSize <= 0) creationSize += 1; planeArray[x][y] = new Plane(colorMaterial, creationSize, creationSize);//plane.clone(); planeArray[x][y].x = -x*scale + maxSize + spacing;//*10 + maxSize + spacing;//planeVector[y-1].x + maxSize + spacing; planeArray[x][y].y = -y*scale + maxSize + spacing;//*10) - maxSize - spacing;//planeVector[y-1].y + maxSize + spacing; trace("xy: " + planeArray[x][y].x + ", " + planeArray[x][y].y); zDepth = _amplitude*brightnessArray[x][y]; //gives a Z-depth in relation to the brightness of the associated pixel, times the _amplitude planeArray[x][y].z = -zDepth; planeContainer.addChild(planeArray[x][y]); } } //pcContainer.addChild(sphere); //planeContainer.addChild(sphere); } /* removes all children in an object @param: object - the object to remove the children from @returns: false if no children are present, true if all children were removed */ private function removeChildren(object:Object):Boolean { if(object.numChildren == 0) { return false; } for(var o:Object in object.children) { object.removeChild(object.children[o]); } return true; } /*I didn't write this part but I only used it for testing purposes...it's fairly simple anyway*/ private function addAxis():void { // Create a default line material and a Lines3D object (container for Line3D objects) var defaultMaterial:LineMaterial = new LineMaterial(0x000000); var axes:Lines3D = new Lines3D(defaultMaterial); // Create a different colour line material for each axis var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000); var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00); var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF); // Create a origin vertex var origin:Vertex3D = new Vertex3D(0, 0, 0); // Create a new line (length 100) for each axis using the different materials and a width of 2. var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 1, origin, new Vertex3D(100, 0, 0)); var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 1, origin, new Vertex3D(0, 100, 0)); var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 1, origin, new Vertex3D(0, 0, 100)); // Add lines to the Lines3D container axes.addLine(xAxis); axes.addLine(yAxis); axes.addLine(zAxis); scene.addChild(axes); } public function set amplitude(value:Number) { _amplitude = value; var zDepth:Number; for(var x:uint = 0; x<=numPlanesWide; x++) { for (var y:uint = 0; y<numPlanesHigh; y++) { zDepth = _amplitude*brightnessArray[x][y]; planeArray[x][y].z = -zDepth; } } } public function get amplitude():Number { return _amplitude; } public function set colorize(value:Boolean):void { if(_colorize!=value) { var x:uint; var y:uint; var newColorMaterial:ColorMaterial; if(_colorize) { for(x = 0; x<=numPlanesWide; x++) { for (var y:uint = 0; y<numPlanesHigh; y++) { newColorMaterial = new ColorMaterial(pixelArray[x][y]); newColorMaterial.doubleSided = true; planeArray[x][y].material = newColorMaterial; } } } else { newColorMaterial = new ColorMaterial(0x000000); newColorMaterial.doubleSided = true; for(x = 0; x<=numPlanesWide; x++) { for (y = 0; y<numPlanesHigh; y++) { planeArray[x][y].material = newColorMaterial;//new ColorMaterial(Math.random()*0xffffff); } } } _colorize = value; } } public function get colorize():Boolean { return _colorize; } /* Takes a pixel and evaluates the brightness */ private function calcBrightness(p:uint):uint { var r:uint = (p&0xFF0000)>>16; var g:uint = (p&0xFF00)>>8; var b:uint = (p&0xFF); //trace((r<<16 | g<<8 | b).toString(2)); //return (Math.sqrt((.241*((p&0xFF0000)*(p&0xFF0000))) + (.691*((p&0xFF00)*(p&0xFF00))) + (.068*((p&0xFF)*(p&0xFF))) )); return (Math.sqrt((.241*(r*r)) + (.691*(g*g)) + (.068*(b*b)))); } /*for rotation*/ private function stage_mouseDownHandler(event:MouseEvent):void { isMouseDown = true; } /*for rotation*/ private function stage_mouseUpHandler(event:MouseEvent):void { isMouseDown = false; } override protected function onRenderTick(e:Event=null):void { /*if(orbitInitialized) { orbitor.update(); }*/ var currentMousePoint:Point = new Point(viewport.containerSprite.mouseX, viewport.containerSprite.mouseY); if(isMouseDown) { var difference:Point = currentMousePoint.subtract(previousMousePoint); var vector:Number3D = new Number3D(difference.x, difference.y, 0); var rotationAxis:Number3D = Number3D.cross(vector, FORWARD); rotationAxis.normalize(); var distance:Number = Point.distance(currentMousePoint, previousMousePoint); var rotationMatrix:Matrix3D = Matrix3D.rotationMatrix(rotationAxis.x, -rotationAxis.y, rotationAxis.z, distance/250); pcContainer.transform.calculateMultiply3x3(rotationMatrix, pcContainer.transform); } previousMousePoint = currentMousePoint; super.onRenderTick(e); } } }