/**
* Copyright FlashBum ( http://wonderfl.net/user/FlashBum )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/1Rjm
*/
package {
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.view.BasicView;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Loader;
import flash.display.Shape;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
import flash.utils.Dictionary;
/**
* author Jesse Freeman aka @theFlashBum | http://jessefreeman.com
*/
public class PV3DInteractionSandbox extends BasicView {
private var material : BitmapMaterial;
private var plane : Plane;
private var rectangles : Dictionary = new Dictionary();
private var currentFocus : String;
private static const PHOTO : String = "photo";
private static const MASK : String = "mask";
private static const TEXTURE : String = "texture";
private static const EDGES : String = "edges";
private static const BASE_URL : String = "http://demos.flashartofwar.com/ImageComposite/images/";
private var loader : Loader = new Loader();
private var currentlyLoading : Object;
private var preloadList : Array = new Array({name:MASK, src:"photo_mask.png"}, {name:PHOTO, src:"photo.jpg"}, {name:TEXTURE, src:"photo_texture.jpg"}, {name: EDGES, src:"photo_edges.png"});
private var layers : Dictionary = new Dictionary(true);
private var context : LoaderContext;
private var finalImageBMD : BitmapData;
private var debug : Boolean = true;
private var mousePoint : Point = new Point();
/**
* This sandbox shows how to take multiple images and create one transparent
* torn image, create a BitmapMaterial out of it and turn it into a 3d
* plane in papervision. Once the plane is rendered we will test for "virtual"
* buttons on it using predefined rectands and the built in colisiion
* detection of the FLash Player.
*/
public function PV3DInteractionSandbox(viewportWidth : Number = 640, viewportHeight : Number = 480, scaleToStage : Boolean = true, interactive : Boolean = true, cameraType : String = "Target") {
super(viewportWidth, viewportHeight, scaleToStage, interactive, cameraType);
init();
}
/**
* Configure camera's focus, set the load context then
*/
protected function init() : void {
camera.focus = 100;
context = new LoaderContext();
context.checkPolicyFile = true;
preload();
}
/**
* Handles preloading our images. Checks to see how many are left then
* calls loadNext or compositeImage.
*/
protected function preload() : void {
if (preloadList.length == 0) {
compositeImage();
createRectangles();
createDemoPlane();
startRendering();
} else {
loadNext();
}
}
/**
* Loads the next item in the prelaodList
*/
private function loadNext() : void {
currentlyLoading = preloadList.shift();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoad);
loader.load(new URLRequest(BASE_URL + currentlyLoading.src), context);
}
/**
* Handles onLoad, saves the BitmapData then calls preload
*/
private function onLoad(event : Event) : void {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoad);
layers[currentlyLoading.name] = Bitmap(event.target.content).bitmapData;
currentlyLoading = null;
preload();
}
/**
* Composit image
*/
private function compositeImage() : void {
// Create Bitmap data for final image
finalImageBMD = new BitmapData(layers[PHOTO].width, layers[PHOTO].height, true, 0xffffff);
// Get width and height for cutting out image
var rect : Rectangle = new Rectangle(0, 0, layers[PHOTO].width, layers[PHOTO].height);
var pt : Point = new Point(0, 0);
// This is our container while we apply the texture
var imageComposit : BitmapData = new BitmapData(layers[PHOTO].width, layers[PHOTO].height, true, 0xffffff);
// Copy pixel data from the photo over to the container using the layers[MASK] to cut out the shape
imageComposit.copyPixels(layers[PHOTO], rect, pt, layers[MASK], null, true);
// Draw on top of container with the texture and apply Darken + Multiply blend modes
imageComposit.draw(layers[TEXTURE], null, null, BlendMode.DARKEN, null, true);
imageComposit.draw(layers[TEXTURE], null, null, BlendMode.MULTIPLY, null, false);
// Copy the edges on top of the the entire compisited image
imageComposit.copyPixels(layers[EDGES], rect, pt, layers[MASK], null, true);
// Copy over the composite image to the BitmapData using the mask to cut out it's shape
finalImageBMD.copyPixels(imageComposit, rect, pt, layers[MASK], null, true);
}
/**
* This creates a simple plane for us to do hit tests on. We take the
* compisited image from above and set it as a BitmapMaterial. Then we
* create the plane, then add InteractiveScene3DEvent listeners. Finally
* we set the camaera to show the plane at 100% size. Picked this trick
* up from Bartek Drozdz here http://www.everydayflash.com/blog/index.php/2008/07/07/pixel-precision-in-papervision3d/
*/
private function createDemoPlane() : void {
trace("Created");
material = new BitmapMaterial(finalImageBMD, true);
material.interactive = true;
if(debug) {
material.bitmap.draw(displayHitArea());
}
plane = new Plane(material, finalImageBMD.width, finalImageBMD.height, 4, 4);
scene.addChild(plane);
plane.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, handleMouseMove);
plane.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, handleMouseDown);
plane.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, onMouseOut);
camera.z = -(camera.zoom * camera.focus) - Math.abs(plane.z);
}
/**
* This is a debug function to visually display the rectangles we are
* testing for.
*/
private function displayHitArea() : Shape {
var rectangle : Rectangle;
var key : String;
var shape : Shape = new Shape();
for (key in rectangles) {
rectangle = rectangles[key];
shape.graphics.beginFill(Math.random() * 0xffffff, .5);
shape.graphics.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
shape.graphics.endFill();
}
return shape;
}
/**
* When the mouse leaves the 3d object we kill any focus it may have had.
*/
private function onMouseOut(event : InteractiveScene3DEvent) : void {
if(currentFocus) {
currentFocus = null;
Mouse.cursor = MouseCursor.ARROW;
}
}
/**
* Here we test that we have focus to determin of a buttonclick is valid.
*/
private function handleMouseDown(event : InteractiveScene3DEvent) : void {
if(currentFocus)
trace("Clicked " + currentFocus);
}
/**
* When we get a mouse move event from a 3d object we save out the x,y
* to our mousePoint and call detectHit.
*/
private function handleMouseMove(event : InteractiveScene3DEvent) : void {
mousePoint.x = event.x;
mousePoint.y = event.y;
detectHit(mousePoint);
}
/**
* This creates some sample buttons for us to test with.
*/
private function createRectangles() : void {
rectangles = new Dictionary(true);
rectangles["button1"] = new Rectangle(10, 10, 40, 40);
rectangles["button2"] = new Rectangle(100, 10, 300, 20);
rectangles["button3"] = new Rectangle(10, 80, 150, 150);
}
/**
* This method checks that we don't have a currentFocus (name of the
* rectangle last rolled over). If a currentFocus is set we test to see
* if the mouse has moved out of the rectangle. When this happens we set
* the currentFocus to null.
*
* If no currentFocus exists we loop through the rectangles dictionary
* and cal container on each rect instance and compare to the mouse x,y.
* When a collision is detected we set the currentFocus to the key of the
* rectangle the detection happen in.
*/
private function detectHit(point : Point) : void {
var rectangle : Rectangle;
var key : String;
// Check to see if anything currently has focus
if(currentFocus) {
if(!withinHitarea(Rectangle(rectangles[currentFocus]), point)) {
//Rectangle(rectangles[currentFocus]).contains(x, y)) {
trace("Lost Focus", currentFocus);
currentFocus = null;
// This is a FP 10 feature that allows us to set the mouse display
Mouse.cursor = MouseCursor.ARROW;
}
} else {
for (key in rectangles) {
rectangle = rectangles[key];
if(withinHitarea(rectangle, point)) {
//contains(x, y)) {
currentFocus = key;
trace("New Focus", key);
// This is a FP 10 feature that allows us to set the mouse display
Mouse.cursor = MouseCursor.BUTTON;
}
}
}
}
/**
* Core hit test logic. Takes a rectangle and a point then tests if the
* point is within the target.
*/
protected function withinHitarea(target : Rectangle, point : Point) : Boolean {
return target.containsPoint(point);
}
/**
* On render we update the mouse rotation and call super render for pv3d.
*/
override protected function onRenderTick(event : Event = null) : void {
updateMouseRotation();
super.onRenderTick(event);
}
/**
* This rotates the plane to "face" the mouse.
*/
protected function updateMouseRotation() : void {
var tmpWidth : Number = finalImageBMD.width;
var tempHeight : Number = finalImageBMD.height;
var rotY : Number = (mouseX - (stage.stageWidth * .5)) / (tmpWidth * .5) * (-20);
var rotX : Number = (mouseY - (stage.stageHeight * .5)) / (tempHeight * .5) * (-20);
plane.rotationX = plane.rotationX + (rotX - plane.rotationX) * .5;
plane.rotationY = plane.rotationY + (rotY - plane.rotationY) * .5;
}
}
}