/* ---------------
// 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);
}
}
}