Here on the blog we are always looking for new technologies to play around with and I seem to have found one that I am going to be spending some time playing with, Away3D (a 3D Flash engine). Personally, I have spent a good deal of my time in the last couple of years looking at 3D, whether it be on the desktop or web, and it is exciting to finally play around with 3D in Flex. The goal of this tutorial is simple, to get you acquainted with Away3D and get a spinning cube rendered on the screen. This is going to be easier than you think.
First we are going to take a look at the small demo running below. As you can see there isn't much to it. We have a panel and inside that we have a spinning cube. One item that isn't noticeable is that we use a UIComponent to hold our 3D scene. I will explain why we do this more later. I created the texture that is being used in Paint.Net and will not go over how it was built in this tutorial. But for now just take a second and stare at the nice spinning cube. You can look at and get the source code for the demo here.
To begin, we are going to step out and grab the latest Away3D source code and compile it into a library we can use. Step one, download the latest source code from here - as of this post they are on version 1.9.4. Once you have it downloaded, extract the source code somewhere you can remember. The next step is to create a Flex Library Project, I did this in Flex Builder 3. This can be performed by right clicking in the "Flex Explorer" area and go to new > Flex Library Project. Go ahead and give it a name, I used Away3D. Now click "Finish".
You should now have a project in your explorer for Away3D. Copy the source code from wherever you extracted it to the project. You should have two root folders, away3d and nochump. With any luck your project looks similar to the one on the right. You should get a compile error though mentioning that you don't have any source code selected. This is solved easily, open up the properties for the project (right click the project and go to properties) and once the window opens go to "Flex Library Build Path". The "Classes" tab should be open and go ahead and check both root folders then click "OK". Consequently you should have your bin folder fill up with your library swc.
Following the compilation of our library we can now get started actually building the demo. Create a new Flex Project and name it. Afterwords, you need to copy your Away3D library swc to the libs folder - this ensures that Flex Builder will handle adding to the project references. In our main application file we are going to add a panel that has 100% width and height, I also added a title. This code is illustrated below.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="500" height="400" >
<mx:Panel width="100%" height="100%" title="Our 3D Canvas"
backgroundColor="#D1F0FF">
</mx:Panel>
</mx:Application>
Next comes the more difficult part - we need to get our 3D view setup. We are going to create a custom component that will handle all the 3D stuff. I actually created a custom Actionscript class named FlexView3D and it extends UIComponent. The reason we extend UIComponent is because the View3D class in Away3D is based on a Sprite and the only Flex component that can add a Sprite as a child is UIComponent. The following code is the base of our class and component.
{
import mx.core.UIComponent;
public class FlexView3D extends UIComponent
{
public function FlexView3D()
{
super();
}
}
}
To finish up the main application we add our FlexView3D to our panel. This results in following complete code for the main application.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"
layout="absolute" width="500" height="400">
<mx:Panel width="100%" height="100%" title="Flex 3D View"
backgroundColor="#D1F0FF">
<local:FlexView3D width="100%" height="100%" />
</mx:Panel>
</mx:Application>
Getting back to our custom class, we start by adding a private variable for our 3D view which is of type View3D. Now we override the protected function createChildren this is where we are going to create the view and add it to our component. You can learn more about creating custom Actionscript components here. The function lies below.
override protected function createChildren():void
{
super.createChildren();
if(!this.view)
{
this.view = new View3D();
this.view.camera.moveTo(new Number3D(0, 0, -1500));
this.view.camera.lookAt(new Number3D(0, 0, 0));
}
this.addChild(this.view);
}
You can see above we check to make sure the view is not already created and then create a new one. The next two lines initialize the 3D camera. It moves the camera to a far away point. This is where the coordinate system comes in, we use +y as up and +z is into the screen. We also tell the camera to look at the center of the screen 0, 0, 0. One item to note is that we have not moved the view yet, really we need to move the view to the middle because its position is used as 0, 0, 0. This centering is done in another function. We override the updateDisplayList function and in there we are guaranteed the size of the overall component is set so we center our 3D view.
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(this.width / 2 != this.view.x)
this.view.x = this.width / 2;
if(this.height / 2 != this.view.y)
this.view.y = this.height / 2;
}
Getting the cube added is the next item in the agenda. This will not be hard either, Away3D has taken the hard graphics pieces out of the equation. We add another private variable for the cube, then add creation and initialization code for it in the createChildren function. This again checks to make sure it is not created yet then creates a cube with a couple of init arguments, one for the name and one for size. We also add a material to the cube (a material defines the look), in this case we add a BitmapFileMaterial which takes a path to an image. It uses that image as the texture for each side. We then add our cube to the scene in the view. The following is added to the createChildren function.
{
this.sotcCube = new Cube({name: "cube", size: 250});
this.sotcCube.material = new BitmapFileMaterial("sotc.jpg");
}
this.view.scene.addChild(this.sotcCube);
There is only one thing left to do, and that is actually spinning the cube and render the scene. We are going to setup a render loop by hooking into the ENTER_FRAME event on our component. We do this in the constructor of the class which updates this to the below code.
{
super();
this.addEventListener(Event.ENTER_FRAME, onFrameEnter);
}
Our event listener onFrameEnter does a couple of things. It checks to see if the view and the view's stage have been created. If both exist then we update the rotationX, rotationY, and rotationZ of the cube which will make it spin. Finally we call render on the view. This completes our component and now we have a spinning 3D cube in Flex. The entire class code is below.
{
import away3d.core.material.BitmapFileMaterial;
import away3d.core.math.Number3D;
import away3d.core.scene.View3D;
import away3d.objects.Cube;
import flash.events.Event;
import mx.core.UIComponent;
public class FlexView3D extends UIComponent
{
private var view:View3D;
private var sotcCube:Cube;
public function FlexView3D()
{
super();
this.addEventListener(Event.ENTER_FRAME, onFrameEnter);
}
override protected function createChildren():void
{
super.createChildren();
if(!this.view)
{
this.view = new View3D();
this.view.camera.moveTo(new Number3D(0, 0, -1500));
this.view.camera.lookAt(new Number3D(0, 0, 0));
}
this.addChild(this.view);
if(!this.sotcCube)
{
this.sotcCube = new Cube({name: "cube", size: 250});
this.sotcCube.material = new BitmapFileMaterial("sotc.jpg");
}
this.view.scene.addChild(this.sotcCube);
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(this.width / 2 != this.view.x)
this.view.x = this.width / 2;
if(this.height / 2 != this.view.y)
this.view.y = this.height / 2;
}
private function onFrameEnter(event:Event):void
{
if(this.view && this.view.stage)
{
this.sotcCube.rotationX += .7;
this.sotcCube.rotationY += .5;
this.sotcCube.rotationZ += .4;
this.view.render();
}
}
}
}
Well I hope you all feel that we accomplished our goal for this tutorial. Plan on seeing more posts concerning Away3D in the future here at Switch on the Code. As always, if you have any questions, concerns, or just plain quips about the tutorial feel free to drop a comment.
04/20/2008 - 07:02
I have downloaded the source provided here and run it successfully! Thank you for sharing your jobs to us! I will pay close attention to "Switch On The Code" and hope you to provide more brilliant articles!
05/31/2008 - 21:24
Hi,
Great article, very helpful.
If you would like to download and use the source code provided here with the latest version of Away3D (2.10) then i found the following import changes worked.
import away3d.core.math.Number3D;
import away3d.lights.AmbientLight3D;
import away3d.containers.View3D;
import away3d.primitives.Cube;
Looking forward to more articles!
07/02/2008 - 19:59
Thanks much for your example! Do you have any thoughts on why the cube can overrun its panel boundaries if its larger than your example?
07/23/2008 - 23:15
Hi,
Wonderful article!
I enjoy reading it and will download and run it!
Thanks a lot for yr help!
08/02/2008 - 12:21
I saw your updateDisplayList method sets the x and y position rather than using setActualsize or move method. Isn't it creating a recursive call ?
08/29/2008 - 03:09
I just downloaded source zip file from source link above provided by You, then opened Flex Bulder 3, File>Import Flex Project and put any bitmap file as "sotc.jpg" in src folder. IT WORKS!!!! I very appreciate and respect people doing great work for others. Thanks.
02/24/2009 - 01:10
{
import away3d.core.material.BitmapFileMaterial;
import away3d.core.math.Number3D;
import away3d.core.scene.View3D;
import away3d.objects.Cube;
import flash.events.Event;
import mx.core.UIComponent;
public class FlexView3D extends UIComponent
{
private var view:View3D;
private var sotcCube:Cube;
public function FlexView3D()
{
super();
this.addEventListener(Event.ENTER_FRAME, onFrameEnter);
}
override protected function createChildren():void
{
super.createChildren();
if(!this.view)
{
this.view = new View3D();
this.view.camera.moveTo(new Number3D(0, 0, -1500));
this.view.camera.lookAt(new Number3D(0, 0, 0));
}
this.addChild(this.view);
if(!this.sotcCube)
{
this.sotcCube = new Cube({name: "cube", size: 250});
this.sotcCube.material = new BitmapFileMaterial("sotc.jpg");
}
this.view.scene.addChild(this.sotcCube);
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(this.width / 2 != this.view.x)
this.view.x = this.width / 2;
if(this.height / 2 != this.view.y)
this.view.y = this.height / 2;
}
private function onFrameEnter(event:Event):void
{
if(this.view && this.view.stage)
{
this.sotcCube.rotationX += .7;
this.sotcCube.rotationY += .5;
this.sotcCube.rotationZ += .4;
this.view.render();
}
}
}
}
02/24/2009 - 10:55
This looks like the code from the tutorial. Did you change something?
03/22/2009 - 07:55
I've just downloaded latest library version for fp9(2.3.3), built it, include in project with your code. And it doesn't work.
If i'm not wrong, Object3D's (and Camera3D's as child class) method 'moveTo' should be called with three parameters (dx:Number, dy:Number, dz:Number).
So you should replace
with
But lookAt method requires one parameter of type Number3D, so I don't know why they did three ususal Number params for moveTo. :\ Does somebody?
Sorry for english, if it's something wrong.
04/22/2009 - 00:06
I'm wondering why, too...
Could somebody enlighten us on this please?
07/09/2009 - 21:08
thanks
09/16/2009 - 22:05
THANKS!
04/06/2010 - 18:17
I am trying to bring up this wonderful Getting started Tutorial using Spark components in Flex_4....
Suffice it to say Adobe has not exactly made the migration path all so easy, of for that matter even documented the seemingly required changes...
So nice that the skinning support adjustment for a panel object have changed in ways that bring about run time flash player errors.. I.m to new at Flex4 to see my way through all of this...... Can any one help in getting this Demo to work with the latest release of Flex_4??
P.S on a different note, the zip file of sources for page seems to be corrupted....
06/26/2010 - 00:58
Hello:
I am still trying out this, in the mean time I have a few questions:
1. Currently one image is applied for all sides of the cub, how do we get each side its own image?
2. Also can we add any flex controls/layouts like canvas, panel, box etc.
Thanks much for for all your work.
- Anand
06/27/2010 - 23:50
It works like charm. Few questions.
* How do we pause the rotation?
* I tried MovieMaterial instead of BitMapMaterial and passed a flv file but it wouldn't work.
Thanks,
Anand
07/01/2010 - 00:46
Hi,
Could you share the solution to your 1st question, if incase you have got one?? I am kind of looking for the same. Having a different image for each side of the cube.
Thanks a ton in advance.
07/07/2010 - 04:28
Folks any idea how to get different images on each of the cube?? I badly need the solution or some hints on going about this..
Thanks.
08/25/2010 - 16:00
When you create a cube you can set each face as a material like so:
new Cube({cubeMaterials:{left:matLeft, right:matRight, top:matTop, bottom:matBottom, front:matFront, back:matBack}, etc etc});
obviously, each material (matLeft and friends) is some kind of ...material. Like a ColorMaterial, or whatever.
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.