Home  >  

FLARVision: Augmented Reality and Papervision

Author photo
AddThis Social Bookmark Button

Overview

The basic explanation of Augmented Reality is to superimpose graphics over real-world environments in realtime. In Flash, this is usually done with a webcam and a marker card. When you hold the card up to the webcam, Flash is able to detect the orientation of the marker and superimpose, in this case, a 3d model on top of it. This technology has been around for a while now and having access to it in Flash is really exciting. Although it is still in its infancy, over time the speed and power of AR will improve to hopefully play an important role in how we view the web. Now is a great time to get your feet wet in order to get a better understanding on how it works.

There is a lot of buzz around Augmented Reality in Flash and it looks like everywhere you turn another person is writing a tutorial about it. In fact this post is one that I used to get me started. You may be thinking what is different about this tutorial from all of the rest? Well for starters, this tutorial is geared towards creating a set of classes that encapsulate the setup of the FLARToolKit as well as creating a debug mode for you to test your markers without a webcam. A quick scroll through the tutorial may seem daunting but I go through each part of the setup, step by step walking you through exactly what is being done.

However, if long tutorials are not your thing or you just like reading the end of the book first I suggest downloading the Final Demo files below to check out what is going on at your own pace. I commented the code so you can easily figure out what is going on.

Download the FLARVision source code - this contains all of the code along with documentation and our example app.

For the rest of you who decided to take the plunge and follow along, you will need the these files to get started:

Download the Resources - this has the marker, camera.dat file and some support images for our tutorial. These files go in your html-template folder or you bin folder (wherever your compiled swf get saved to).

Before we get started here is some terminology you may not be familure with:

  • FLARToolKit is the name of the Library we will use in order to implement AR in our project. This library was created by Saqoosha and is based on NyARToolkit 2.0.0. It is availible as open source under the GNU General Public License
  • PV3D is the abbreviation of Papervision 3D. This is a realtime 3D engine also available as open source from here.
  • FLARVision is the name of the demo we will be creating. This is a combination of the names FLARToolKit and Papervision 3D.
  • camera_para.dat is a binary file that is required by the FLARToolkit in order to urn. It represent setting and configurations values for web cams.
  • Marker or Pattern is the name of the graphic FLAR will analyze and calculate its orientation from. This graphic is a special box with a think black border and a graphic inside. You need this in order to get AR working.
  • CardEmulator is a simple class that will act like a printed marker card when you don't have a web cam or are doing testing. We will emulate how a person would hold the marker up to the camera based on mouse movement.

Now that you have seen the jargon, lets take a look at what you will be building:

flarvision_demo

Click on the above image to view the demo. You will also need to print out this marker to use with your webcam.

Setting up Papervision 3D and FLARToolKit

This tutorial was created using Flex Builder 3 but can be easily adapted to work with Flash CS 4, FDT, or Flash Develop. If you are using Flex Builder you will need a SVN plug. Here are the libraries we will need and how I set them up.

Checkout Papervision 3D trunk. I check this out as a Flex Library called Papervision3D

svn checkout http://papervision3d.googlecode.com/svn/trunk/as3/trunk
checkout_pv3d

After Papervision is checked out you will need to right click on the project and select Properties to finish configuring it. In the Flex Library Build Path make sure you check src. This will create a SWC of the library in the bin folder.

configure_pv3d

Now we will need to checkout the FLARToolkit. I also checked this out as a Flex Library and named it FLARToolKit.

svn checkout http://www.libspark.org/svn/as3/FLARToolKit/trunk
flartoolkit_checkout

Once FLARToolKit has been checked out, we will need to customize its properties as well. Make sure you check src for the Build Path and we need to deselect org.libspark.flasrtoolkit.core.FLARVersion class from getting compiled into the SWC. For some reason this class throws several errors that keep the SWC from building correctly.

flartoolkit_config

You will also need to add a reference to the Papervision3D project which can be done in the Library path tab by selecting Add Project. Once that is done you should have no errors and FLARToolKit + Papervision3D will be correctly configured to create SWCs for us to use.

include_pv3d_lib

Creating our Doc Class

Now we will create a new ActionScript Project called FLARVision. You will need to add the Papervision3D and FLARToolKit projects into FLARVision's Library Path.

The first thing we are going to do is set up a simple Doc Class. This class will be the base for any project you want create using the FLARToolKit and Papervision. I have constructed this so it is easy to extend by overriding the core setup methods when you are ready to build your own FLAR projects. Lets take a look:

 
/**
 * Original Author:  Jesse Freeman
 * Class File: FLARVision.as
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * Revisions
 * 		1.0  Initial version April 29, 2009
 *
 */
 
package
{
 	import flash.display.Sprite;
 	import flash.display.StageAlign;
 	import flash.display.StageQuality;
 	import flash.display.StageScaleMode;
 	import flash.events.Event;
 	
 	import org.papervision3d.cameras.Camera3D;
 	import org.papervision3d.materials.WireframeMaterial;
 	import org.papervision3d.objects.primitives.Plane;
 	import org.papervision3d.render.BasicRenderEngine;
 	import org.papervision3d.scenes.Scene3D;
 	import org.papervision3d.view.Viewport3D;
 	
 	/**
 	 * 
 	 * @author Jesse Freeman
 	 *  
 	 */	
 	public class FLARVision extends Sprite
 	{
  		protected var scene:Scene3D;
  		protected var camera:Camera3D;
  		protected var viewport:Viewport3D;
  		protected var renderer:BasicRenderEngine;
  		
  		/**
  		 * Main Constructor for the class.
  		 * 
  		 */		
  		public function FLARVision()
  		{
   			configureStage();
   			init();
   		}
  		
  		/**
  		 * Configures the stage and sets the scale mode to keep visuals from
  		 * distorting when resizing the browser window.
  		 * 
  		 */			
  		private function configureStage():void  
          {  
              	stage.quality = StageQuality.HIGH;
               stage.align = StageAlign.TOP_LEFT; 
               stage.scaleMode = StageScaleMode.NO_SCALE; 
           }
          
  		/**
  		 * Called at the construction of the class. Calls the setup Papervision
  		 * method and adds an event listener to the render the Viewport.
  		 * 
  		 */             
  		protected function init():void {
   			setupPapervision();
   			addEventListener(Event.ENTER_FRAME, renderViewport);
   		}
  		
  		/**
  		 * Sets up and configures Papervision. This function can be overridden
  		 * to accommodate any custom set ups you may need for your project.
  		 * 
  		 */			
  		public function setupPapervision():void
  		{
   			scene = new Scene3D();
   			camera = new Camera3D();
   
   			// Create the Viewport
   			viewport = new Viewport3D(stage.stageWidth, stage.stageHeight);
   			addChild(viewport);
   
   			create3dObjects();
   			
   			renderer = new BasicRenderEngine();
   		}
  		
  		/**
  		 * This default function is where 3d Objects should be added to PV3D's
  		 * scenes. 
  		 * 
  		 */			
  		protected function create3dObjects():void
  		{
   			var plane:Plane = new Plane( new WireframeMaterial(0xff0000) );
   			scene.addChild(plane);
   		}
  		
  		/**
  		 * Renders the papervision scene.
  		 * @param event
  		 * 
  		 */				
  		public function renderViewport(event:Event = null):void
  		{	
   			renderer.renderScene(scene, camera, viewport);
   		}
  		
  	}
}

If everything went according to plan you should see the following image, a simple wireframe plane on a blue background. This is a really basic setup for Papervision and a lot of this should be self explanatory. If this is your first foray into the land of 3d and Flash lets go over the basics.

flarvision_preview_1

The first thing you need from PV3d is a Scene3d. This is where all of your 3d objects get attached to. Next we set up a Camera that acts as the perspective of our 3d scene. The camera is usually the object you manipulate when you want things to scroll left to right or you want to fly forward and backwards through 3d space. Finally we have the viewport, this is the actual display of the 3d world and what you add to the display list. Here we are setting the width and hight of the viewport to the match the stage.

After we add the viewport to the stage we call create3dObjects which will represent the stub function where we add 3d objects onto the stage. The last thing we need to complete our PV3D setup is a renderer. There are several renderers available, in this tutorial we will use two different ones, but for now let stick to the BasicRenderEngine.

FLAR Pattern Emulator

Before we get crazy with setting up the Web Cam and the FLARToolKit we should create a testing environment. This is important for a few reasons:

  • We can set up a predictable environment for us to prototype our ideas in.
  • By creating a test environment we can fine tune our program without any dependancies such as connecting to the webcam or printing out our test pattern.
  • Having a solid debug mode helps validate the stability of our code and rule out external problems before doing live testing.
  • Testing with the webcam is a pain. Every time you recompile your project you have to accept permission to use the webcam. With a debug mode we can build everything out then do our final testing with webcam once we know everything works.

So what will this emulator do? As we talked about earlier, we need a marker to analyze. This emulator will create a simple 3d plane with our test marker as its texture. We will also use the mouse to rotate our card in 3d space to simulate what a user would do when interacting with their own webcam. So lets create a new class called CardEmulator in a com.insideria.flar package.

create_card_emulator

Here is the code:

/**
 * Original Author:  Jesse Freeman
 * Class File: CardEmulator.as
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * Revisions
 * 		1.0  Initial version April 29, 2009
 *
 */
 
package com.insideria.flar
{
 	import flash.display.Sprite;
 	
 	import org.papervision3d.cameras.Camera3D;
 	import org.papervision3d.materials.BitmapFileMaterial;
 	import org.papervision3d.objects.primitives.Plane;
 	import org.papervision3d.render.BasicRenderEngine;
 	import org.papervision3d.scenes.Scene3D;
 	import org.papervision3d.view.BitmapViewport3D;
 
 	/**
 	 *	The CardEmulator represent a simple system for testing an AR Marker with
 	 * needing to use a webcam as a source. This class creates an instance of a
 	 * Papervision 3D renderer, viewport and scene with a simple 3d plane skinned
 	 * with a texture of your marker. When render is called the mouse is tracked
 	 * and the plane is adjusted to test your pattern at different angles.

* * This since this class requires a reference to stage for the mouse * calculations you must attach it to the stage or another display object. * In cases when you don't want to see the viewport you can optionally toggle * addViewportToDisplay in the constructor. * * @author Jesse Freeman * */
public class CardEmulator extends Sprite { protected var _width:Number = 0; protected var _height:Number = 0; protected var testMarkerURL:String; protected var emulatorViewport:BitmapViewport3D; protected var emulatorRenderer:BasicRenderEngine; protected var emulatorScene:Scene3D; protected var emulatorCamera:Camera3D; protected var testCard:Plane; protected var addViewportToDisplay:Boolean = false; /** * Returns an instance of the viewport as a BitmapViewport3d object. * * @return BitmapViewport3D and can be used to sample BitmapData from. * */ public function get viewport():BitmapViewport3D { return emulatorViewport; } /** * Constructs the emulator environment. We need a url to the test * marker, a width and a height. * * @param testMarkerURL loads in a sample test marker image. * @param w width of the emulators display - default 320. * @param h height of the emulators display - default 240. * @param addViewportToDisplay tells the emulator if it should attach * the viewport to the display or not. In most cases you would not want * to set this to true unless you are testing that the Emulator is * actually displaying and working. * */ public function CardEmulator(testMarkerURL:String, w:Number = 320, h:Number = 240, addViewportToDisplay:Boolean = false) { this.testMarkerURL = testMarkerURL; _width = w; _height = h; this.addViewportToDisplay = addViewportToDisplay; init(); } /** * @private * * On init we create the emulators viewport, render scene and camera. * We also attach a testCard (plane) to the scene to act as our sample * pattern. * */ protected function init():void { // Setup PV3D emulatorViewport = new BitmapViewport3D(_width, _height); emulatorRenderer = new BasicRenderEngine(); emulatorScene = new Scene3D(); emulatorCamera = new Camera3D(); // Create test pattern plane var bmpMaterial:BitmapFileMaterial = new BitmapFileMaterial(testMarkerURL, true); bmpMaterial.doubleSided = true; testCard = new Plane(bmpMaterial, 300, 300, 4, 4); // Make the camera face the testCard emulatorCamera.target = testCard; emulatorScene.addChild( testCard ); // Make sure we should add this to the display if(addViewportToDisplay) addChild(emulatorViewport); } /** * * Here we take the mouse's movement and rotate the camera * accordingly. This assumes that the CardEmulator instance has a * reference to the stage. * */ protected function calculateMouseMovement():void { if(stage) { var rotY: Number = (mouseY-(stage.stageHeight/2))/(stage.height/2)*(2200); var rotX: Number = (mouseX-(stage.stageWidth/2))/(stage.width/2)*(-2200); emulatorCamera.x = emulatorCamera.x + (rotX - emulatorCamera.x) / 2; emulatorCamera.y = emulatorCamera.y + (rotY - emulatorCamera.y) / 2; } } /** * * When a render is called we calculate the mouseMovement then render * out the scene. * */ public function render():void { calculateMouseMovement(); emulatorRenderer.renderScene(emulatorScene, emulatorCamera, emulatorViewport); } } }

This setup is very similar to what we did before in our Doc Class. It's important to note that we are using a BitmapViewport3D so that later we can easily sample the BitmapData from it. Also, we create a testCard from a Plane and add a BitmapFileMaterial for the texture. The last thing we have is the render function and calculateMouseMovement methods. Notice how this class does not have an EnterFrame listener to run the render? That is because this function will be called externally by the FLARVision class from its own render function. This way we keep the render loops down to a minimum especially when running two instances of Papervision at the same time.

Now we are ready to set this class up in our FLARVision doc class. Lets add the following function after our create3dObjects method:

/**
 * Creates the emulator card to use in debug mode.
 * 
 */
protected function createEmulatorCard():void
{
 	cardEmulator = new CardEmulator("images/flarlogo.gif", 320, 240, true);
 	addChild(cardEmulator);
}

We are also going to need to add the following variable at the top of our class:

protected var cardEmulator:CardEmulator;

As well as import the CardEmulator class:

import com.insideria.flar.CardEmulator;

Before we can test that our emulator works, lets change our init function to look like this:

protected function init():void {
 	createEmulatorCard();
 	setupPapervision();
 	addEventListener(Event.ENTER_FRAME, renderViewport);
}

and modify our renderViewport to call the CardEmulator's render method as well:

public function renderViewport(event:Event = null):void
{	
 	cardEmulator.render();
 	renderer.renderScene(scene, camera, viewport);
}

Before you compile its important to mention that you should copy over the data and images folders into the FLARVIsion's html-template folder or where ever you keep you compiles swfs. You can get these files from the resourced zip at the beginig of the project. Once you do this you may need to clean your project under Project -> clean ... inorder for Flex Builder to copy them over to your bin directory. Here is how your project should be set up.

html_template_setup

If you compile the project and check it out you should see our test Pattern Emulator in the upper left corner of the screen. As you move the mouse, the test pattern will rotate.

flarvision_preview_2

If you get the following Sandbox error here is how we can fix it:

SecurityError: Error #2148

You will need to right click on the flash movie in your browsers, after you have ignored the security error and click on the Settings option.

fp_settings

Next you will need to go to the Global Security Settings Panel and click Edit Locations... From there put in the path to your bin-debug folder or the place you will be launching the swf from.

security_settings

Now if you return to the browser and hit refresh the security error should go away.

It is important to note that we are only making the test pattern 320x240. As we get deeper into this tutorial you will start to notice performance slow downs when running the FLARToolKit. AR is still very new to Flash and the code that determines the position of the pattern still needs to be optimized. When we add a webcam feed into the equation things really slow down at higher resolutions. To combat that, we will always be sampling the video source at 320x240. This will keep the performance up and give you the best possible results. Later we will add some code to upscale the source image but now lets look at how to set up the FLARToolKit.

Creating An AR Detector

When following some of the example out there on setting up the FLARToolKit its easy to lose focus of exactly what is going on. I was completely lost myself until I went line by line and tried to figure out the best setup. I always strive to build simple, standalone classes that can be easily reused or extended to fit a particular project's needs. That is why I encapsulated the setup of the FLARToolKit into its own class I call the ARDetector. Lets create this class in the com.insideria.flar package.

create_ardetector

Here is the code:

/**
 * Original Author:  Jesse Freeman
 * Class File: ARDetector.as
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * Revisions
 * 		1.0  Initial version April 29, 2009
 *
 */
 
package com.insideria.flar{
 	
 	import flash.display.BitmapData;
 	import flash.events.Event;
 	import flash.events.EventDispatcher;
 	import flash.events.IEventDispatcher;
 	import flash.events.IOErrorEvent;
 	import flash.events.SecurityErrorEvent;
 	import flash.net.URLLoader;
 	import flash.net.URLLoaderDataFormat;
 	import flash.net.URLRequest;
 	
 	import org.libspark.flartoolkit.core.FLARCode;
 	import org.libspark.flartoolkit.core.param.FLARParam;
 	import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData;
 	import org.libspark.flartoolkit.core.transmat.FLARTransMatResult;
 	import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector;
 	
 	/**
 	 * The ARDetector is a manager for the FLARToolKit and helps facilitate
 	 * the setup, configuration, and detection of markers while using the 
 	 * FLARToolKit's underlining core classes.
 	 * 
 	 * This class is a modification of an example on 
 	 * http://saqoosha.net/en/flartoolkit/start-up-guide
 	 * 
 	 * 
 	 * @author Jesse Freeman
 	 * 
 	 */	
 	public class ARDetector extends EventDispatcher{
  		
  		protected var cameraURL:String;
  		protected var markerURL:String;
  		protected var codeWidth:int;
  		protected var _flarParam:FLARParam;
  		protected var code:FLARCode;
  		protected var raster:FLARRgbRaster_BitmapData;
  		protected var detector:FLARSingleMarkerDetector;
  
  		public var width:int;
  		public var height:int;
  		public var markerWidth:Number = 16;
  		public var markerHeight:Number = 16;
  		
  		/**
  		 * 
  		 * This is the ARDetector consturctor. By default we set the width and
  		 * height to 320 x 240 to match our video source.
  		 * 
  		 * @param canvasWidth set to 320 by default
  		 * @param canvasHeight set to 240 by default
  		 * @param codeWidth set to 80 by default. This represents the width of the marker.
  		 * 
  		 */		 				
  		public function ARDetector(canvasWidth:int = 320, canvasHeight:int = 240, codeWidth:int = 80) {
   			width = canvasWidth;
   			height = canvasHeight;
   			this.codeWidth = codeWidth;
   		}
  		
  		/**
  		 * This will return an instance of the FLARParam.
  		 * 
  		 */		
  		public function get flarParam():FLARParam
  		{
   			return _flarParam;	
   		}
  		
  		/**
  		 * This sets the BitmapData src to be monitored for patterns.
  		 * 
  		 */		
  		public function set src(target:BitmapData):void
  		{
   			// setup ARToolkit
   			raster = new FLARRgbRaster_BitmapData(target);
   			detector = new FLARSingleMarkerDetector(_flarParam, code, codeWidth);
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Loads in the camera.dat file. This file contains information about
  		 * the webcam.
  		 *  
  		 * @param url path to camera.dat file.
  		 * 
  		 */		 	
  		protected function loadCameraFile(url:String):void
  		{
   			var camLoader:URLLoader = new URLLoader();
   				camLoader.dataFormat = URLLoaderDataFormat.BINARY;
   			
   			camLoader.addEventListener(Event.COMPLETE, onCameraFileLoad, false, 0, true);
   			addErrorListeners(camLoader);
   			
   			camLoader.load(new URLRequest(url));	
   			
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Triggered on a successful camera.dat file load. Once a complete 
  		 * event is received we remove the listeners, create a new FLARParam,
  		 * and set it's screen size. Finally we load the marker.
  		 * 
  		 * @param event
  		 * 
  		 */		 	
  		protected function onCameraFileLoad(event:Event):void
  		{
   			event.stopImmediatePropagation();
   			
   			var target:URLLoader = event.target as URLLoader;
   			
   			target.removeEventListener(Event.COMPLETE, onCameraFileLoad);
   			removeErrorListeners(target);
   			
   			_flarParam = new FLARParam();
   			_flarParam.loadARParam(target.data);
   			_flarParam.changeScreenSize(width, height);
   			
   			loadMarkerFile(markerURL);
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * This loads the marker.pat file. The marker represents the image we
  		 * look for from the src BitmapData.
  		 * 
  		 */		
  		protected function loadMarkerFile(url:String):void
  		{
   			var patLoader:URLLoader = new URLLoader();
   				patLoader.dataFormat = URLLoaderDataFormat.TEXT;
   				
   			patLoader.addEventListener(Event.COMPLETE, onMarkerFileLoad, false, 0, true);
   			addErrorListeners(patLoader);
   			
   			patLoader.load(new URLRequest(url) );
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Triggered when a marker.pat file is loaded. We remove the listeners
  		 * create a new 16x16 FLARCode instance, and pass in the pattern. This
  		 * represents the resolution of your marker. By default 16x16 works for
  		 * lower detailed patterns. After this the ARDetector is configured and 
  		 * dispatches a Complete event.
  		 * 
  		 * @param event
  		 * 
  		 */		 	
  		protected function onMarkerFileLoad(event:Event):void
  		{
   			event.stopImmediatePropagation();
   			
   			var target:URLLoader = event.target as URLLoader;
   			
   			target.removeEventListener(Event.COMPLETE, onCameraFileLoad);
   			removeErrorListeners(target);
   			
   			code = new FLARCode(markerWidth, markerHeight);
   			code.loadARPatt(target.data);
   
   			dispatchEvent(new Event(Event.COMPLETE, true, true) );
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Adds error event listeners and re-dispatches them once received.
  		 * 
  		 */		
  		protected function addErrorListeners(target:IEventDispatcher):void
  		{
   			target.addEventListener(IOErrorEvent.IO_ERROR, dispatchEvent);
   			target.addEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent);
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Removes error event listeners.
  		 * 
  		 */		
  		protected function removeErrorListeners(target:IEventDispatcher):void
  		{
   			target.removeEventListener(IOErrorEvent.IO_ERROR, dispatchEvent);
   			target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent);
   		}
  		
  		/**
  		 * In order to set up the FLARDetector we will need two things: the 
  		 * camera.dat and the marker.pat files. The camera.dat
  		 *  
  		 * @param cameraURL this is the path to the camera.dat file.
  		 * @param markerURL this is the path to the marker.pat file.
  		 * 
  		 */		 		    
  		public function setup(cameraURL:String, markerURL:String):void {
   			this.cameraURL = cameraURL;
   			this.markerURL = markerURL;
   			loadCameraFile(cameraURL);
   		}
  		
  		/**
  		 * 
  		 * This calculates the transformation Matrix to match the found
  		 * marker's coordinates.
  		 * 
  		 * @param resultMat - a type of matrix that can be used to store values 
  		 * of the detected marker's orientation.
  		 * 
  		 */		 	
  		public function calculateTransformMatrix(resultMat:FLARTransMatResult):void
  		{
   			detector.getTransformMatrix(resultMat);
   		}
  		
  		/**
  		 * 
  		 * This validates if we have found a marker from the set src (raster).
  		 * It returns a true/false based on the supplied values. You must supply 
  		 * a source for this to work correctly.
  		 * 
  		 * @param threshold The threshold value to be used for detecting the marker.
  		 * @param confidence This is how confident the detector is that it found
  		 * a marker.
  		 * 
  		 */		
  		public function detectMarker(threshold:int = 90, confidence:Number = .5):Boolean
  		{
   			return (detector.detectMarkerLite(raster,threshold) && detector.getConfidence() > confidence)
   		}
  
  	}
}

As we set this class up in the FLARVision Doc Class I will go over the important public functions you need to get up and running. The other functions have comments to help explain what is going on under the hood. Back in our FLARVision class we will initialize our ARDetector with the following two methods after the configureStage function:

/**
 * Creates the AR Detector class and have it load in the camera.data
 * and pattern.pat files.
 * 
 */        
protected function createFlarDetector():void
{
 	arDetector = new ARDetector();
 	arDetector.addEventListener(Event.COMPLETE, onActivate);
 	arDetector.setup('data/camera_para.dat', 'data/flarlogo.pat');
}

/**
 * 
 */        
protected function onActivate(event:Event):void
{
 	init();
}

Lets also add a new protected variable for the ARDetector to the top of our class:

protected var arDetector:ARDetector;

And import the ARDetector class:

import com.insideria.flar.ARDetector;

So far we have created a new instance of the ARDetector, then added an event listener to monitor for the complete event signaling that the camera.data and pattern.pat are loaded. We also added an onActivate handler that calls init. Since we want this to run before anything else happens we are going to need to alter our constructor a little to look like this:

public function FLARVision()
{
 	configureStage();
 	createFlarDetector();
}

Now if you run this and check the browser's connections you should see that two additional files have been loaded but nothing has happened.

files_loading

Next we will connect up a source to the ARDetector.

Creating a Capture Source

The capture source represents BitmapData that the ARDetector will scan to determine if a valid marker is present. We will add the following method under our onActivate handler:

/**
 * Creates a Bitmap for us to scan for valid markers.
 * 
 */		
protected function createCaptureSource():void
{
 	capturedSrc = new Bitmap(new BitmapData(arDetector.width, arDetector.height, false, 0), PixelSnapping.AUTO, true);
 	arDetector.src = capturedSrc.bitmapData;
 	addChild(capturedSrc);
}

We will also need a captureSrc Bitmap variable to store our captureSrc:

protected var capturedSrc:Bitmap;

and we have to import the following classes:

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.PixelSnapping;

Next we will call this function before we call init in the onActivete handler. Change it to look like this:

protected function onActivate(event:Event):void
{
 	createCaptureSource();
 	init();
}

This next function will handle updating the captureSrc with BitmapData from our cardEmulator. Add the following after the renderViewport method:

/**
 * Updates the capturedSrc with new bitmap data.
 * 
 */		
protected function updateCaptureBitmap():void
{
 	capturedSrc.bitmapData.draw(cardEmulator.viewport);
}

Notice how we are drawing BitmapData from the cardEmulator's viewport to our capturedSrc? This setup will allow us to swap out the cardEmulator for the user's webcam. For now we have a few more things to add in order to get this working. Lets continue by calling updateCaptureBitmap in our render loop. Change renderViewport to this:

public function renderViewport(event:Event = null):void
{	
 	cardEmulator.render();
 	updateCaptureBitmap();
 	renderer.renderScene(scene, camera, viewport);
}

The last thing we need to do is tell the emulator that it doesn't need to add the viewport to it's display list. There is a fourth parameter in the constructor that is set to false by default so lets replace the entire line in the createEmulatorCard method:

cardEmulator = new CardEmulator("images/flarlogo.gif", 320, 240, true);

with this

cardEmulator = new CardEmulator("images/flarlogo.gif");

Remember that the captureSrc is now sampling BitmapData directly from the emulator's viewport so there is no need to display the viewport in the CardEmulator. If you run it now, everything should look the same except the emulator is now being draw into our captureSrc bitmap. To make sure we only have one display being rendered you can try setting the captureSrc's x over 360 px. If you see the following you have both the CardEmulator and the captureSrc being displayed and that is bad.

double_src_render_problem

Great, now we have a capture source, and our emulator is running so lets connect it up to the ARDetector.

Connecting The ARDetector To A Source

These next few steps are going to go a lot quicker. You're doing great so hang in there because this is our last major hurdle. Lets add the marker detection logic to our render loop by replacing renderViewport with the follow:

public function renderViewport(event:Event = null):void
{	
 	cardEmulator.render();
 	updateCaptureBitmap();
 	
 	try
 	{
  		if (arDetector.detectMarker()) {
   			arDetector.calculateTransformMatrix(resultMat);
   			baseNode.setTransformMatrix(resultMat);
   			active();
   		}
  		else
  		{
   			inactive();
   		}
  	}
 	catch(errObject:Error) {
    		trace(errObject.message);
  	}
 	
 	renderer.renderScene(scene, camera, viewport);
}

/**
 * Displays the base node when a marker has been found.
 */		
protected function active():void
{
 	if(!isActive)
 	{
  		isActive = true;
  		baseNode.visible = true;
  	}
}

/**
 * Hides the base node when a marker can't be found.
 */		
protected function inactive():void
{
 	if(isActive)
 	{
  		baseNode.visible = false;
  		isActive = false;
  	}
}

We will need some new variables:

protected var resultMat:FLARTransMatResult = new FLARTransMatResult();
protected var isActive:Boolean = false;
protected var baseNode:FLARBaseNode;

and import the following classes:

import org.libspark.flartoolkit.core.transmat.FLARTransMatResult;
import org.libspark.flartoolkit.pv3d.FLARBaseNode;

So lets look at what is going on. Every time we call renderViewport (onEnterFrame) we will test that the ARDetector has detected a marker. If it did, we will ask the ARDetector to update a matrix that we will then apply to our baseNode. The base node will represent the main object all Papervision 3d Objects should be added to. Next we update the orientation of the baseNode to match the detected marker's own orientation. This is all taken care of for us by the FLARToolKit. If the marker is still detected we will call the active method. Active simply shows the base node and if there is no marker we hide the base node by calling inactive.

Let setup our base node by changing the setupPapervision method to look like this:

public function setupPapervision():void
{
 	scene = new Scene3D();
 	camera = new FLARCamera3D(arDetector.flarParam);
 
 	// Create the Viewport
 	viewport = new Viewport3D(stage.stageWidth, stage.stageHeight, true);
 	addChild(viewport);
 	
 	// The base node is where all PV3D object should be attached to.
 	baseNode = new FLARBaseNode();
 	scene.addChild(baseNode);
 	
 	create3dObjects();
 	
 	renderer = new BasicRenderEngine();
}

Here we are creating the baseNode and adding it to the scene. Also, we have changed the camera to a FLARCamera3D which requires a reference to the flarParam from our ARDetector to property position it. Make sure you import the FLARCamera3D class.

import org.libspark.flartoolkit.pv3d.FLARCamera3D;

The FlarParam is a matrix that matches the orientation of the detected marker and the FLARCamera3d is a custom class from the FLARToolKit that help facilitate reorienting the Camera bases on the flarParam's values.

Next we will need to update our default Plane to be attached to the base node instead of the scene then rotate it so it faces the camera correctly. Replace the create3dObjects method with the follow:

protected function create3dObjects():void
{
 	var plane:Plane = new Plane( new WireframeMaterial(0xff0000), 80, 80 );
 	plane.rotationX = 180;
 	baseNode.addChild(plane);
}

We made it! Our ARDetector is now correctly seeing our marker from the CardEmulator. You can verify that this is working by doing a compile and moving the mouse to the edges of the screen.

flarvision_preview_3

Notice that the test plane sometime disappears? This is the detector losing the marker and making the baseNode invisible. To help illustrate how we can map this 3d object over the emulator's display lets make the captureSrc match the dimensions of the viewPort. If we replace the

protected function configureStage():void  
{  
    	stage.quality = StageQuality.HIGH;
     stage.align = StageAlign.TOP_LEFT; 
     stage.scaleMode = StageScaleMode.NO_SCALE;
     stage.addEventListener(Event.RESIZE, onStageResize); 
}

protected function onStageResize(event:Event = null):void
{
 	if(capturedSrc)
 		resize(capturedSrc, stage.stageWidth, stage.stageHeight);
 	if(viewport)
 		resize(viewport, stage.stageWidth, stage.stageHeight);
}

/**
 * A function to resize any DisplayObject.
 *
 * @param target
 * @param areaWidth
 * @param areaHeight
 * @param aspectRatio
 * 
 */		
public function resize(target : DisplayObject, areaWidth : Number, areaHeight : Number, aspectRatio : Boolean = true, autoCenter:Boolean = true) : void {
 	
 	if(aspectRatio) {
  		
  		var sw : Number = areaWidth;
  		var sh : Number = areaHeight;
  		var tw : Number = target.width;
  		var th : Number = target.height;
  	
  		var si : Number;
  		//
  		if(sw > sh) {
   			si = sw / tw;
   			if(th * si > sh)
   			si = sh / th;
   		}else {
   			si = sh / th;
   			if(tw * si > sw )
   			si = sw / tw;
   		}
  	
  		var wn : Number = tw * si;
  		var hn : Number = th * si;
  	
  		target.width = wn;
  		target.height = hn;
  	}else {
  		target.width = areaWidth;
  		target.height = areaHeight;
  	}
 	
 	if(autoCenter)
 	{
  		target.x = ((areaWidth * .5) - (target.width * .5));
  		target.y = ((areaHeight * .5) - (target.height * .5));
  	}
}

We will also need to import the DisplayObject class:

import flash.display.DisplayObject;

and add the following line to the end of createCaptureSource method to force a resize of the src and the viewport:

onStageResize();

Now compile the app and you will see how the baseNode maps itself on top of the captureSrc.

flarvision_preview_4

Next we will replace the captureSrc with video from a webcam. One thing you may need to do is offset the viewport to make the PV3D Object correctly overly the marker.

Setting Up A Webcam

Setting up a webcam as a source is easier then one would imagin. While we set up a webcam as a src we are also going to put in the foundation for switching debug mode on and off. Lets start by adding the following function under create3dObjects:

/**
 * Creates a camera or emulator to use as the src for the ARDetector
 * to analyze for markers.
 * 
 */		
protected function createCamera():void
{
 	if(debug || !Camera.getCamera())
 	{
  		createEmulatorCard();	
  	}
 	else
 	{
  		webcam = Camera.getCamera();
  		webcam.setMode(arDetector.width, arDetector.height, 30);
  		video = new Video(arDetector.width, arDetector.height);
  		video.attachCamera(webcam);
  	}
}

What this does is check to see if debug mode is active or if there is no camera to detect and creates the CardEmulator. If debug mode is false we create a new Camera instance and set it up. The video represents the video feed from the webcam so we set it up to match the width and height of the ARDetector's canvas (320x240) and attach the webcam instance to it. You will also need the following variables:

protected var debug:Boolean = false;
protected var webcam:Camera;
protected var video:Video;

and import these classes:

import flash.media.Camera;
import flash.media.Video;

Next in our init method lets replace:

createEmulatorCard();

with

createCaptureSource();
createCamera();

Now all we need to do is remove cardEmulator.render() from our update our render loop like this:

public function renderViewport(event:Event = null):void
{	
 	updateCaptureBitmap();
 
 	try
 	{
  		if (arDetector.detectMarker()) {
   			arDetector.calculateTransformMatrix(resultMat);
   			baseNode.setTransformMatrix(resultMat);
   			active();
   		}
  		else
  		{
   			inactive();
   		}
  	}
 	catch(errObject:Error) {
    		trace(errObject.message);
  	}
 	
 	renderer.renderScene(scene, camera, viewport);
}

and change our updateCaptureBitmap to this:

protected function updateCaptureBitmap():void
{
 	if(debug || !video)
 	{
  		cardEmulator.render();
  		capturedSrc.bitmapData.draw(cardEmulator.viewport);
  	}
 	else
 	{
  		capturedSrc.bitmapData.draw(video);
  	}
}

Now if you compile your app you will be asked to use a webcam (assuming you have one) and you will see a live feed of the webcam being overlaid by our sample plane.

use_webcam

You can now switch between the the emulator and the webcam by changing the debug variable.

Well that about covers the technical hurdles of getting the FLARToolKit and Papervision up and running. The rest of this tutorial will go over cleaning up what we have created and extend this demo to show you how to build your own applications from this foundation.

Creating A Simple App

This is where the fun comes in. Now that we have the foundation in place we can simply build off of this and customize only what we need. Lets start by creating a new Doc Class called ARcPod (cPod stands for Cardboard iPod from my FlashBum Website).

create_arcpod_class

If you are in Flex Builder right click on the file and set it as your Default Application.

set_as_default

Here is what your Doc Class should look like:

/**
 * Original Author:  Jesse Freeman
 * Class File: ARcPod.as
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * Revisions
 * 		1.0  Initial version April 29, 2009
 *
 */
 
package
{
 	
 	import com.insideria.CPodContainer;
 	
 	import flash.events.Event;
 	import flash.events.MouseEvent;
 	import flash.text.TextField;
 	import flash.text.TextFormat;
 	
 	[SWF( backgroundColor="#000000", framerate="31" )]
 	public class ARcPod extends FLARVision
 	{
  		protected var label:TextField;
  		protected static const defaultText:String = "Click to change to debug mode - Debug is currently to ";
  		
  		/**
  		 * 
  		 */		
  		public function ARcPod()
  		{
   			super();
   		}
  		
  		/**
  		 * Calls the super's init then creates our debug display and adds a
  		 * click listener to the stage. 
  		 * 
  		 */		
  		override protected function init():void
  		{
   			super.init();
   			createDebugDisplay();
   			stage.addEventListener(MouseEvent.CLICK, onClick);
   		}
  		
  		/**
  		 * Creates our 3d object the cPod.
  		 * 
  		 */		
  		override protected function create3dObjects():void
  		{
   			var cpod:CPodContainer = new CPodContainer();
   			cpod.attachTo(baseNode);
   		}
  		
  		/**
  		 * Switches the active source from the CardEmulator to the Webcam.
  		 * 
  		 */		
  		public function switchSource():void
  		{
   			if(debug)
   			{
    				if(contains(cardEmulator))
    				{
     					removeChild(cardEmulator);
     					cardEmulator = null;
     				}	
    			}
   			else
   			{
    				video = null;
    				webcam = null;				
    			}
   			
   			debug = !debug;
   			label.text = defaultText+debug;
   			createCamera();
   		}
  		
  		/**
  		 * Sets up a display to let us know what mode we are in.
  		 * 
  		 */		
  		protected function createDebugDisplay():void
  		{
   			label = new TextField();
   			label.defaultTextFormat = new TextFormat("Arial", 12, 0x000000, true);
   			label.autoSize = "left";
   			label.selectable = false;
   			label.background = true;
   			label.text = defaultText+debug;
   			addChild(label);
   		}
  		
  		/**
  		 * Class switch source when a click event is recieved. 
  		 * @param event
  		 * 
  		 */		
  		protected function onClick(event:MouseEvent):void
  		{
   			switchSource();
   		}
  		
  	}
}

You will also need to create the CPodContainer class in com.insideriawhich is a wrapper that encapsulates the creation of the actual PV3D Object.

create_cpod_class

Here is the code:

 
/**
 * Original Author:  Jesse Freeman
 * Class File: CPodContainer.as
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * Revisions
 * 		1.0  Initial version April 29, 2009
 *
 */
 
package com.insideria
{
 	import org.papervision3d.materials.BitmapFileMaterial;
 	import org.papervision3d.materials.utils.MaterialsList;
 	import org.papervision3d.objects.DisplayObject3D;
 	import org.papervision3d.objects.primitives.Cube;
 	
 	/**
 	 * The CPodContainer is a manger for a Papervision cube that represents
 	 * the cPod. This container class simply sets up the cube, loads in the
 	 * textures and attaches it to a supplied target DisplayObject3D.
 	 * 
 	 * This simple setup allows you to customize the logic behind creating
 	 * a cPod instance while maintaining encapsulation and extensibility.
 	 * 
 	 * @author Jesse Freeman
 	 * 
 	 */	
 	public class CPodContainer
 	{
  		protected var cube:Cube;
  		
  		/**
  		 * Main constructor. Calls init and begins creating the cPod 3d 
  		 * Object.
  		 * 
  		 */		
  		public function CPodContainer()
  		{
   			init();
   		}
  		
  		/**
  		 * @private
  		 * 
  		 */		
  		protected function init():void
  		{
   			createCube();
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * Creates a simple cube primitive that will represent our cPod.
  		 * 
  		 */		
  		protected function createCube():void
  		{
   			cube = new Cube(createSkins(), 198, 29, 391, 10, 4, 10);
   			cube.roll(-90);
   			cube.z = 20;
   			cube.scale = .5;
   		}
  		
  		/**
  		 * @private
  		 * 
  		 * @return a MarterialsList with BitmapFileMaterials for each face: front,
  		 * back, left, right, top and bottom.
  		 * 
  		 */		
  		protected function createSkins():MaterialsList
  		{
   			var frontBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-front.jpg");
   			var backBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-back.jpg");
   			var rightBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-left.jpg");
   			var leftBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-right.jpg");
   			var topBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-top.jpg");
   			var bottomBFM:BitmapFileMaterial = new BitmapFileMaterial("images/skins/cpod-bottom.jpg");
   			
   			return new MaterialsList({front: frontBFM, back: backBFM, right: rightBFM, left: leftBFM, top: topBFM, bottom: bottomBFM});
   		}
  		
  		/**
  		 * This is similar to addChild except it will attach the cPod's 3d
  		 * instance (a cube) onto a target DisplayObject3D.
  		 * 
  		 * @param target
  		 * 
  		 */		
  		public function attachTo(target:DisplayObject3D):void
  		{
   			target.addChild(cube);
   		}
  	}
}

Now if you run the ARcPod class you will see our cardboard iPod now shows up when our marker is detected.

flarvision_preview_5

We simply overrode the create3dObjects method from the FLARVision class and added our new object. You can make this as complex as you want, cpu permitting. I also added in some additional logic to handle switching back and forth between our debug mode. Just remember to add all of your 3d objects onto the baseNode and you will be set.

Optimizations

This setup is by no means production ready but it is not far off. Lets take a look at a few optimization changes we can do to keep thinking running smoothly:

  • Screen Resolution - In this example I decided to show you how to create a scalable display but through my experiments I found this is not practical. Calculating the marker is very intensive and only fast computers can run the demo at a decent clip. This leaves us with two solutions: 1) Don't go full screen and lock your resolution in at 640 x 480 or 800 x 600 or 2) Keep your papervision object (the one we use to composite over the detected marker) at full screen but make your source not stretch. This removes some of the fun with overlaying the graphics directly on top of the video feed but it still lets you take advantage of AR's amazing way of interacting with 3d Objects.
  • Missing Functionality - right now we assume that this is always going to be the same setup for PV3D. What happens if we want to add interactivity. It would probably be good to set up a few of the configurable values as variables instead of hardcoding them. This should be an easy task and the more FLAR apps you build the better you will be able to customize this class to meet your needs. In my own Apps I usually take advantage of a config file to quickly make changes to the final product without having to recompile. Think about what you will be changing from project to project and add that customization into the FLARVision class.
  • Low Quality - One trick we can use to speed up PV3D is to lower the stage quality. You wouldn't see any noticeable degradation in quality. I usually do this unless I am working with a lot of animated texted or upscaling images.

Creating your own markers

We really didn't go over how to create our own marks at all. This could be a whole other tutorial. Here is a link to one of the best site I could find that explains how to set up your own custom marker.

Conclusion

You should now have all the code you need to set up the FLARToolKit, as well as a good code base to connect it to Papervision. Although this could have been done in less lines of code having our ARDetector and CardEmulator broken out into individual classes helps encapsulate the logic and adds the greatest possibility for extending the system. As you saw from our ARcPod demo we were able to set up a new FLAR app with very little code. I hope this has simplified the setup of your next Augmented Reality project and I would love to see your experiments or answer any question you may have by leaving a comment.

Read more from Jesse Freeman. Jesse Freeman's Atom feed TheFlashBum on Twitter

Comments

29 Comments

samBrown said:

very thorough tutorial, this is great help getting started with AR

rob ford said:

Really impressive tutorial, congrats and thanks for writing it!

rob ford said:

Really impressive tutorial, congrats and thanks for writing it!

ford rob said:

Thanks for writing it, really impressive tutorial, congrats!

dennis said:

great dude!
Will try this in the next few days,
excited to see that you worked out the example so good,
will this also work if you project a video file instead of the 'ipod'?
thanks

Jesse Freeman said:

Thanks for the feedback everyone I really appreciate it and would love to see what you come up with.

Dennis, you can substitute the cardboard ipod for any 3d Object in Papervision including a plane. You could easily map a video onto a plane's surface as an animated MovieClip material and have that display Video. In the base class, FLARVision, I used a plane with a wireframe texture to get started so you could test it out with that. Let me know how it works out...

dennis said:

thanks for responding that soon,
I'm pretty new to this way of programming, so I have some ideas regarding MovieMaterial, but don't know how and where to implement them.
Maybe you can send me an email with an example?

dennis said:

Jesse,
Can you please help me with the above question? Been searching the entire web, but no idea how to do it.
There are lot's of resources, but no one is as detailed as yours is.
Thanks

Jesse Freeman said:

** Doesn't look like this worked the first time I submitted it **

Have a look at this Tutorial I just dug up http://professionalpapervision.wordpress.com/2009/03/09/adding-video-to-a-papervision3d-curved-plane/

It talks about a VideoStreamMaterial. Aparently I had overlooked this class and was thinking of an older way of adding video to a plane. This actually looks like it will simplify things for you since you can apply it just like a normal material. Here is a reference to the VideoStreamMaterial source code as well http://code.google.com/p/papervision3d/source/browse/trunk/as3/trunk/src/org/papervision3d/materials/VideoStreamMaterial.as

In their example they create a new instacne of the material:

videoMaterial = new VideoStreamMaterial(myVideo, myStream);
videoMaterial.doubleSided = true;

and then use the material to skin a curved plane. You will probably want to use the basic plane but it looks like a well document tutorial with source code as well.

Let me know if this helps? When searching for Papervision help I usually use "Papervision 2" then what ever it is I am looking for. It is hard to tell the difference between the older tutorial for PV3D and the new 2.0 version.

Marc Van Norden said:

This was my first dive into AR. This tutorial walks you thru it great.
Early on I ran into the problem of getting the papervision svn downloaded. It would put the folders outside of the src folder I directed it at. I fixed it by moving them manually.

Thanks for taking the time to go into this in detail!

Pan said:

Thanks for the tutorial.

Unfortunately I am having some issues. I started through the tutorial up to configuring the FLARToolKit. I unchecked FLARVersion and added Papervision3D path, but I still have 3 errors.

1. The definition of base class Camera3D was not found. FLARToolKit/src/org/libspark/flartoolkit/away3d FLARCamera3D.as line 42

2. The definition of base class ObjectContainer3D was not found. FLARToolKit/src/org/libspark/flartoolkit/away3d FLARBaseNode.as line 38

3. Method marked override must override another method. FLARToolKit/src/org/libspark/flartoolkit/away3d FLARCamera3D.as line 129

To walk you through my FLAR and PV3D setup steps:
1. Downloaded the svn to FLEX.

2. Created FLEX Project Files for PV3D and FLAR

3. Downloaded files straight to PV3D and FLAR FLEX project files folders. When it was done doing svn check out there were duplicates of the files in the FLEX project folders and outside of them, so I deleted the ones that were not in the FLEX project folders.

4. Checked src folder for PV3D and unchecked FLARVersion for FLAR.

That resulted in the 3 errors mentioned above. I do not understand why this error is occurring, so any help would be appreciated.

Thanks.

Scott Blackburn said:

Thanks for the tutorial, I am also getting the same error as Pan above. It looks like it could be something wrong with the away3D classes?

Any help would be great.

Thanks,

Scott

Jesse Freeman said:

Pan & Scott, thanks for your comments. A few things may have changed in the FLARToolKit since I wrote this tutorial. At the moment I am a little tied up with a new baby so I am not able to update this tutorial to reflect the changes but here is a quick thought on how to fix the problem.

You may have to go through the packages in the Flex Library Build Path panel and deselect the Away3d classes. It looks like he has added in support for other Flash 3d Engines but when I originally did this tutorial there was only Papervision support. What I usually do is follow the "problems" Flex Builder give me in the Flex Library Project and deselect the classes causing the problem. Also, you can set up Away3d just like I did with Papervision, link FLARToolKit to the Away3d Flex Library and that should fix most of the issues. Then you will get a SWC of the FLARToolKit that will support Papervision and Away3d.

As soon as I get a free moment I will look into the issue and update the tutorial accordingly.

Peter said:

Hello Scot Blackburn and Pan,

I had the same errors as you guys described. In my case this was because the autocompletion in my Flexbuilder automatically imported the wrong Camera3D classes. the correct imports in your program should be:

import org.libspark.flartoolkit.pv3d.FLARBaseNode;
import org.libspark.flartoolkit.pv3d.FLARCamera3D;

instead of getting the FLARCamera3D from the alternativa and away3d folders in the flartoolkit

hope this helps you!

regards, Peter Peerdeman

Gelson Reinaldo said:

Hi guys,

I'm trying to publish on my web site a flartoolkit sample. The program works locally but nothing happens when I publish on my site, just a blank screen. What settings (if) a need to do for run on my web server. This is probably something simple but I not found how to resolve.
I appreciate your help!

Gelson

Karlo Campos said:

Hi,

I tried the tutorial and was able to make it work locally. But when you deploy the output files (html's, swf's, etc...) into a webserver it fails to work. What I get is a black blank screen and it doesn't ask the user about allowing the site to access the camera and microphone. This is similar to Gelson's problem.

Regards,
Karlo

Jesse Freeman said:

That is strange because the same demo embedded as the example is built from the same code from the tutorial. I have been meaning to go through this tutorial from scratch when I can find some time and update it to reflect the changes to building the FLARToolKit lib. I will check out these other issues as well. Thanks for the heads up.

If you have a link that would help as well so I can see what the issue may be.

Kiran said:

Hi Jesse,

Thanks a lot for sharing this. I really like this article /blog.

Now I am able start my project after going through your tutorial.

Glen said:

How do I add the libraries? I have downloaded Papervision3D and FLARToolkit. But I can't figure out how to add them as libraries to my project.

Toby said:

Great tutorial, thanks for your efforts. Just wanted to note that I was having the same problem as Gelson and Carlo and it was apparently beign caused by my host (GoDaddy) not recognizing the .dat extension on a windows server. Tried the same files on one of their linux servers without change and the camera was being accessed fine.

I look forward to your next tutorial! Cheers

Toby said:

To expand on that last post, if you are hosting on a windows server make sure to check/update/create your web.config file to include the .dae, .pat and .dat mime types.

Zach Kane said:

Hey Jesse, just wanted to say thanks for the amazing job on this tut; best FLAR writeup I've looked over.

Seems like a lot of problems (could) come from code completion jumping the gun on these libraries. I used Flash Develop so I had to just grab the SWCs the communities had up, worked like a charm though.

merry said:

Pretty much informative article, being webmaster i want to create markers for cheap webhosting blog, introduction of connecting The AR detector to a Source is helpful for me.I have created my own docs class on dedicated server just by follow your instruction.

Mith said:

Hi there mate,

First of all, I am pretty new at all this. However, a very nice and well worked out tut. After some bug fixing I've finally gotten things up and running on my end. However, I have a question.

What I really love about this tut is the debug mode. I was actually trying to get the debug mode to work with another project of mine. However, I can't seem to get it to work.

The other project is based on "Augmented Reality with FLARManager" from gotoandlean.com. I am sure ye are famliar with it.

The thing I am actually trying to do is.. instead of showing a 3d cube. I want to show a video. Can you give me a hint on how to do so in this tut?

Thanks again :)

dbam said:

Hi here,

i'm really having a hard time setting flartoolkit at capturing with a highres webcam at 800x600...
As far i change from the defaults of 320 x 240 i get "off-marker" positioning of the scene i'm trying to draw.
I have the SWF, the webcam, the _raster:FLARRgbRaster and _param:FLARParam, the pv3d render set to 800x600, but the render appears to be offset in 3d space...(the model is at 0,0,0)

I'm getting real close to the deadlines and i don't have a clue...
Any ideas, please?

Jesse Freeman said:

Hey everyone, I am sorry it has taken me so long to update this. I have recently set up a new site dedicated to this tutorial with updated code and the same Cardboard iPod demo. Here is a link to it http://flaremulator.flashartofwar.com and I will be updating this post to reflect some of the changes. If you are looking for the swcs for FLARToolkit and Papervision because the setup I talk about in this tutorial no longer work, please check out the new source code on github and take them from there.

Hope this helps,
Jesse

Simon said:

It seems to me that FLARToolkit has a new release. The NyARToolKitAS3 requires Flash Player 10 because it is using Vectors. After checking out all the files and compiling it, it seems like the transformation matrix isn't getting calculated. Also, the threshold on the getConfidence and such is very different, much lower than the predefined 0.5. Could you please let me know which version of FLARToolkit you were using?

Thanks!

Sérgio Silva said:

Hi,
Is it possible to use an external video image (like a ip camera or a video server), instead of the webcam attach to the computer?

Fahim said:

I have a small question. How can I add a webcam into my website for users to view my AR work?

Leave a comment


Type the characters you see in the picture above.


Tag Cloud

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.