In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

PV3d Bitmap Material Button Emulator

author Jesse Freeman aka @theFlashBum | http://jessefreeman.com
/**
 * 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;
		}
	}
}