This tutorial article is a Step by step
guide for creating a PicLens type 3D photo viewer with Flex and Away3d.
This is step 1, of a 3 part tutorial that will sweep many useful
techniques used in web application design, Flex, and Flash 3D design.
In the final the result will be this.
Requirements
- Adobe Flash CS3
- Adobe Flex Builder 3
- Away3d Library (I recommend using the version I used)
- Tweener Library
- Source Files
Pre Requisites
- Intermediate programming skills
- Moderate knowledge of AS3
- Basic Familiarity with OOP
- Basic knowledge of Flash CS3
Step 0
In this tutorial I will assume that you
are familiar with the basics of building an Actionscript Away3D
project in Flex. If you haven’t used these two technologies together
before, it would be best if you go through my previous tutorial
first. Due to the complexity of this application, I will need to skip
some things to go faster. So, if something is not explained here, or
suddenly a large chunk of code is dropped with no descriptions, it has
been covered (hopefully) in the mentioned tutorial.
Step 1 – New Project
Lets get started. Create a new
Actionscript Project in Flex and name it “PhotoViewer3D”. Then,
download the libraries I listed in the requirements and paste them in
the project’s folder.
Now, one of the things that will be
covered here is a very simplified application architecture that could
be a good starting point for any project you do that implies a moderate
level of complexity. For this, all code will be placed in a folder
structure, carefully separating functions between different classes, and
hence emulating the basics of a Model View Controller design pattern.
You should have a general grasp of it by the end of this series.
Right click on the project’s folder in
the Flex Navigator and choose New -> Folder, then type
“com/li/photoviewer/view” and press Finish. Naming folders this way is
known as Reverse Domain Syntax, and is a good programming practice.
Step 2 – Setting up the scene
Go to PhotoViewer3D.as and build a basic main class:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import com.li.photoviewer.view.TestScene; [SWF(backgroundColor="0x000000", frameRate="30")] public class PhotoViewer3D extends Sprite { public function PhotoViewer3D() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; var testScene:TestScene = new TestScene(); addChild(testScene); } } }
Line 7 imports an away3D holding class
that we haven’t made yet, so we should create it right away. Right
click on the “view” folder and choose New -> Actionscript Class and
name it “TestScene”. It should have the following code in it:
package com.li.photoviewer.view { import away3d.cameras.Camera3D; import away3d.containers.Scene3D; import away3d.containers.View3D; import away3d.core.clip.RectangleClipping; import away3d.core.math.Number3D; import flash.display.Sprite; import flash.events.Event; public class TestScene extends Sprite { private var scene:Scene3D; private var camera:Camera3D; private var view:View3D; public function TestScene() { addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true); } public function init(evt:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); initScene(); initObjects(); addEventListener(Event.ENTER_FRAME, renderScene); } private function initScene():void { scene = new Scene3D(); camera = new Camera3D(); camera.zoom = 10; camera.focus = 200; camera.z = -2000; view = new View3D(); view.camera = camera; view.scene = scene; view.x = stage.stageWidth/2; view.y = stage.stageHeight/2; view.clip = new RectangleClipping(-stage.stageWidth/2, -stage.stageHeight/2, stage.stageWidth/2, stage.stageHeight/2); addChild(view); } private function initObjects():void { } private function renderScene(evt:Event):void { camera.x = 3*(mouseX - stage.stageWidth/2); camera.y = 3*(mouseY - stage.stageHeight/2); camera.lookAt(new Number3D(0, 0, 0)); view.render(); } } }
NOTE: If any of this shocked you, it has all been explained in the tutorial mentioned in Step 0.
Step 3 – Setting up our PhotoLoader component
Create a new Actionscript Class in the
view folder and name it “PhotoLoader”. These will be our 3D photograph
object. The class will extend Away3D’s Plane primitive, with a few
additional features, more particularly, the ability to load an external
image. So, lets start by setting up the plane:
package com.li.photoviewer.view { import away3d.primitives.Plane; public class PhotoLoader extends Plane { private var urlPath:String; public function PhotoLoader(urlPath:String) { super(); this.urlPath = urlPath; this.rotationX = 90; this.width = 640; this.height = 480; this.segmentsH = 4; this.segmentsW = 4; } } }
As you can see, we’ve set up the
constructor so that when we instantiate a PhotoLoader object, we pass
it the image name it has to load as a parameter. Now, go back to
TestScene, and in the “iniObjects()” method, make an instance of
PhotoLoader and add it to the scene:
var testPhoto:PhotoLoader = new PhotoLoader("imgs/testImage.jpg"); scene.addChild(testPhoto);
The urlPath we passed this time will be
irrelevant, but we’ll pass it anyway so no errors are thrown. Go ahead
and test the application, you should see something like this:
Step 4 – Skinning the plane with Flash
First of all, we need to give the plane
a skin or texture. Even though it will contain an image, it needs to
contain something else while it’s loading, and somehow show progress
for that load process. For this, we will design some graphics in Flash,
and bring them to Flex with what I find to be a very practical and
simple technique. We will use a fla file to generate a library.swf file
that will contain some graphics for our application, and then embed
that swf in Flex and extract the clips as we need them. This is a very
useful technique, and I definitely recommend it for any Flex projects
that contain design based vector graphics.
Open Flash, create a new AS3 Flash File
and save it in your project’s folder, next to PhotoViewer3D.as. Once
in it, select File -> Publish Settings and type “assets/library.swf”
for the swf output path (deselect the html output). Now, create a new
folder next to that fla called “assets” either from your file explorer
or by right clicking the project’s folder in the Flex Navigator. Now,
if you publish the fla you will see that library.swf shows up in your
newly created assets folder.
Step 5 – Creating and extracting the graphics created in Flash
Great, now I wont be describing how to
make the graphics in Flash since I believe you must be very familiar
with this. You should download the fla
and overwrite the one you just made. Sorry! Just wanted to make sure
you understood what’s going on. Now, open the downloaded fla. You will
see a clip called “LoaderFront” made up of a gray box and a progressBar
clip which has in turn a sub clip. The important thing to notice here
is that LoaderFront has a Linkage and its Base Class was changed to
Sprite. Publish it again, go back to Flex, right click on library.swf in
the assets folder and choose Refresh.
This is the only code you’ll need to
pull those graphics from Flex, type it at the top of the PhotoLoader
class (inside Public class…):
[Embed(source="assets/library.swf", symbol="LoaderFront")] public var LoaderFront:Class;
You’ve just extracted the symbol from
the swf’s library and assigned it to a LoaderFront class. If you create
a new instance of this class (and add it to the display tree), you’ll
have a graphics object that extends Sprite! And you can treat it as any
other sprite. Great isn’t it?
Step 6 – Applying the graphics to the plane
Go back to PhotoLoader.as and create these variables for the class:
private var materialSprite:Sprite; private var progressBar:Sprite; private var progressBarSubClip:Sprite;
And then this method:
private function initClips():void { materialSprite = new LoaderFront(); progressBar = Sprite(materialSprite.getChildByName("progressBar")); progressBarSubClip = Sprite(progressBar.getChildByName("subClip")); }
Now we have references to all the clips
that we need. You have to add the “initClips()” call at the end of the
constructor class.
Next, type this new method under the last one and also make sure you call it from the constructor:
private function initMaterial():void
{
var material:MovieMaterial = new MovieMaterial(materialSprite);
material.smooth = true;
this.material = material;
}
And Test the application. You should now
see that the plane has taken the graphics as its material. Your
PhotoLoader class should look like this so far:
package com.li.photoviewer.view { import away3d.materials.MovieMaterial; import away3d.primitives.Plane; import flash.display.Sprite; public class PhotoLoader extends Plane { [Embed(source="assets/library.swf", symbol="LoaderFront")] public var LoaderFront:Class; private var urlPath:String; private var materialSprite:Sprite; private var progressBar:Sprite; private var progressBarSubClip:Sprite; public function PhotoLoader(urlPath:String) { super(); this.urlPath = urlPath; this.rotationX = 90; this.width = 640; this.height = 480; this.segmentsH = 4; this.segmentsW = 4; initClips(); initMaterial(); } private function initClips():void { materialSprite = new LoaderFront(); progressBar = Sprite(materialSprite.getChildByName("progressBar")); progressBarSubClip = Sprite(progressBar.getChildByName("subClip")); } private function initMaterial():void { var material:MovieMaterial = new MovieMaterial(materialSprite); material.smooth = true; this.material = material; } } }
Step 7 – Loading images into the plane
The final thing to do for Part 1 of
this tutorial, is implement all the image loading features of our
extended plane. For this, we will use a regular Loader instance and add
a few event listeners to check for progress and complete events.
Add the loader variable next to the other class variables:
private var loader:Loader;
And call a new method under the “initMaterial()” call in the constructor as “initLoader()”, then build the method:
private function initLoader():void { loader = new Loader(); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler); loader.contentLoaderInfo.addEventListener(Event.INIT, initHandler); materialSprite.addChild(loader); loader.alpha = 0; }
The loader is initialized, two listeners
are registered, then the loader is injected to the plane’s material
sprite completely invisible. I’ve made it invisible because I’m going
to make it fade in once the image has been loaded into it.
Now, lets set up the handlers and a little “setProgress()” utilitary method:
private function progressHandler(evt:ProgressEvent):void
{
setProgress(evt.bytesLoaded/evt.bytesTotal);
}
private function initHandler(evt:Event):void
{
Tweener.addTween(loader, {alpha:1, time:1, transition:"easeoutexpo"});
}
private function setProgress(value:Number):void
{
progressBarSubClip.scaleX = value;
}
Pretty straight forward. As the image
downloads, the progress handler calls the “setProgress()” function
which in turn sets the horizontal scale of the progress bar, and when
the download completes, or more precisely when the loaded content is
initialized, Tweener makes the loader fade in by animating its alpha.
Don’t forget to import Tweener for this:
import caurina.transitions.Tweener;
Step 8 – Triggering the load
Next, lets trigger the load by adding a “loadImage()” method as the last method called in the constructor and defining it:
private function loadImage():void { loader.unload(); loader.alpha = 0; setProgress(0); loader.load(new URLRequest(urlPath)); }
Finally, create the imgs folder in the project, download THESE images and paste them in it. Test the app, you should see the image loaded on the plane:
Watch the Demo HERE.
NOTE: If you have any security issues at this point you should disable the compilers network for now. Do this by right clicking the projects folder, choosing properties, then clicking on the Actionscript Compiler tab and in the Additional Compiler Settings, type “-use-network=false”. Don’t forget to disable this when you go online… This is something anyone using Flex gets to know one way or the other for quickly getting over any sandbox issues. Next:
On the next part we will continue to
develop our application by using multiple instances of this PhotoLoader
object, developing advanced camera motion and interactivity.
0 comments:
Post a Comment