Flex 4 with Away3D 3.5.0

In this post I will try to explain how I integrated Away3D engine with Flex 4 project. I am not Flex or Away3D guru, so please let me know if you find any errors. Also I am looking forward to read any suggestions how to improve my implementation.

My project uses:

My Away3DComponent represents the 3D view. I based it on SpriteVisualElement, which seems to be most suitable for storing View3D in Flex 4 project. The general idea of my component looks like this:

<xml version="1.0" encoding="utf-8">
<s:SpriteVisualElement xmlns:fx="http://ns.adobe.com/mxml/2009">
 xmlns:s="library://ns.adobe.com/flex/spark"
 xmlns:mx="library://ns.adobe.com/flex/mx"
 addedToStage="addedToStageHandler(event)"
 removedFromStage="removedFromStageHandler(event)">

 <fx:Script&gt;
 <![CDATA[

// Imports
// Variables for camera and 3d view
// Initialization
// Methods
// etc.

 ]]>
 </fx:Script>

</s:SpriteVisualElement>

Its not a class, because I would like to use it as MXML tag.

Below I will try to explain main concepts. Lets start with attributes:

			private var _camera:HoverCamera3D;
			private var _view:View3D;

			// Variables to rotate the camera
			private var _moveCamera:Boolean = false;
			private var _lastPanAngle:Number;
			private var _lastTiltAngle:Number;
			private var _lastMouseX:Number;
			private var _lastMouseY:Number;

			// Additional flags
			private var _isViewportAddedToStage:Boolean = false;
			private var _isMouseOnScene3D:Boolean = true;

The most important here is _view, which contains objects like camera, scene 3D, renderer type and few more. Its like a window to look at 3D scene. HoverCamera3D (as name may suggest) is a class that represents camera. It has useful methods for to rotate camera around 3D object. More about Away3D cameras you can find here: http://www.flashmagazine.com/tutorials/detail/away3d_basics_the_cameras. This is a very good introduction to Away3D and I learned a lot from it.

There are also implemented attributes for camera rotation, to store last mouse position, camera tilt and pan. Boolean variable _moveCamera is determining when the camera should be rotated and when not. To check if the cursor is over the 3D view, I set _isMouseOnScene3D flag.

Component initialization:

			private function initListeners():void
			{
				addEventListener(Event.ENTER_FRAME, onEnterFrame);

				stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
				stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
				stage.addEventListener(Event.RESIZE, onResize);
				stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);

				onResize();
			}

First, some listeners are added to the component. Implementation of event handlers can be found in the source code. Link to zipped project is located at the end of this post.

			private function initializeCamera():void
			{
				_camera = new HoverCamera3D();
				_camera.focus = 200;
				_camera.distance = 200000;
				_camera.yfactor = 1;
				_camera.minTiltAngle = -80;
				_camera.maxTiltAngle = 80;
				_camera.panAngle = 140;
				_camera.tiltAngle = 20;
				_camera.hover(true);

				_view.camera = camera;
			}

Then camera is created and initialized with parameters such as focus, distance, pan and tilt angle (more about them you can learn from article linked earlier).

			private function initialize():void
			{
				initListeners();
				initializeCamera();

                                _view.renderer = new BasicRenderer();

				addChild(_view);

				var awayStats:AwayStats = new AwayStats(_view);
				awayStats.y = 160;
				awayStats.x = 7;

				addChild(awayStats);
			}

Insertion of View3D in component hierarchy is preceded by creation of a new basic renderer. AwayStats is small sprite-based component, which shows some useful information about FPS (Frame Per Secon), memory usage etc.

Now I have a simple component which can be used in my application. Below are snippets from the main application file Away3DExample.mxml :

			// Embeded model textures
			[Embed(source="assets/models/textures/car.png")]
			private var _CarTexture : Class;
			[Embed(source="assets/models/textures/van.png")]
			private var _VanTexture : Class;
			[Embed(source="assets/models/textures/fighter.png")]
			private var _FighterTexture : Class;

			// Embeded 3D models
			[Embed(source="assets/models/car.md2", mimeType="application/octet-stream")]
			private var _CarMD2 : Class;
			[Embed(source="assets/models/van.md2", mimeType="application/octet-stream")]
			private var _VanMD2 : Class;
			[Embed(source="assets/models/fighter.md2", mimeType="application/octet-stream")]
			private var _FighterMD2 : Class;

You can see here some embedded textures and models. Go here to find out more about MD2 model format: http://tfc.duke.free.fr/coding/md2-specs-en.html

			private var _md2: Md2;

MD2 file loader provided by Away3D.

			// Constants for model names
			public static const CAR:String = "Car";
			public static const VAN:String = "Van";
			public static const FIGHTER:String = "Fighter";

CAR, VAM, FIGHTER are string constants. Each represents model name.

			[Bindable]
			private var _models:ArrayList;

Array for loaded and parsed 3D models.

			protected function parseModel(md2Model:Class, texture:Class, name:String):Mesh
			{
				_md2 = new Md2();

				var _model:Mesh = _md2.parseGeometry(md2Model) as Mesh;
				_model.ownCanvas = true;

				// Adjusts the pivot point of the object so that it lies at the center of it's geoemtry
				_model.centerPivot();

				// Setting zero position for model
				_model.x = _model.z = _model.y = 0;

				var bitmapBodyMaterial:BitmapMaterial = new BitmapMaterial(new texture().bitmapData);
				bitmapBodyMaterial.smooth = true;

				_model.material = bitmapBodyMaterial;
				_model.name = name;

				return _model;
			}

In this method, MD2 model is parsed into Away3D Mesh object. Every Mesh is centered, gets texture and name.

			protected function initializeApplicationHandler(event:FlexEvent):void
			{
				away3DComponent.view = new View3D();

				_models = new ArrayList();
				_models.addItem(parseModel(_CarMD2, _CarTexture, CAR));
				_models.addItem(parseModel(_VanMD2, _VanTexture, VAN));
				_models.addItem(parseModel(_FighterMD2, _FighterTexture, FIGHTER));
			}

In this initialization method, new instance of View3D class is created. Parsed models are added to array collection.

			protected function list_changeHandler(event:IndexChangeEvent):void
			{
				if(_lastAddedModelName) away3DComponent.view.scene.removeChildByName(_lastAddedModelName);
				var model:Object3D = modelList.selectedItem as Object3D;
				_lastAddedModelName = model.name;
				away3DComponent.view.scene.addChild(model);
			}

In application I used <s:List> to create menu with model names. Every time selected model is changed, list_ChangeHandled(event:IndexChangeEvent):void is lunched. In this handler previous model is removed  from scene and new one is inserted.

	<components:Away3DComponent id="away3DComponent">

	<s:Panel width="190" id="panel" title="Select model"
			 mouseOver="{away3DComponent.isMouseOnScene3D = false}"
			 mouseOut="{away3DComponent.isMouseOnScene3D = true}>"

		<s:layout>
			<s:VerticalLayout paddingLeft="0" paddingRight="0" paddingTop="0" paddingBottom="0">
		</s:layout>

		<s:List id="modelList" dataProvider="{_models}"
				labelField="name"
				width="100%"
				change="list_changeHandler(event)" borderVisible="false">

	</s:Panel>

You can see here that tag <components:Away3DComponent id=”away3DComponent”/> is declared first so it’s under all other components. Above my Away3DComponent is declared <s:Panel> with vertical layout. Every time mouse is over panel, flag _isMouseOnScene3D is set to false, so camera can’t rotate around model.

You can test working app here (“view source code” is enabled):

Click on image to test example.

And directly download project from here: Away3DExample.zip

I encourage you to familiarize yourself with the future version of Away3D “molehill”, which will use new 3D API, available in the next major version of Flash Payer. Information can be found here: http://away3d.com

Please forgive me mistakes. I am still learning to write in English.