Creating a 3D panorama in Flex using Papervision3D 2.0 GreatWhite (part 1)
example
example showing outline of cube (thin pink lines)
source
Using papervision3D for 3D panoramas achieves the best results I
have seen out there so far. It’s way better than the quicktime VR, and
other methods that have extreme “fisheye” distortion, an unnatural look
& feel, and can make the viewer feel sick to look at it.
There are other examples of this technique out there (here,here ,here )I certainly didn’t invent this, but I’ll try to explain it a little, so here it goes:
I’m going to do this in two parts, because there are two distinct
phases to a panorama project like this, namely developing the
papervision3D environment, and the actual taking and processing of the
photos. We’ll save the photography part for later, and focus on the
code for now.
For this project I found images on the web to use just by googling images for “cube panorama”. So for now we’ll just use those.
The basic steps for creating the panorama is as follows:
1) create or obtain images of a 360 panorama mapped to the inside six
faces of a cube.(as stated for now we’re just taking for granted that
we’ve done this)
a note on the photos:
If you decide to browse the web for a cube panorama photo, they’re
usually presented as a single image of an exploded cube, I opened this
in photoshop, duplicated the background layer 6 times (cmd+J). then
selected the desired square in each layer using a rectangular marquee,
hit select inverse (shift+cmd+i) then deleted the rest. So you end up
with six layers (plus the background which is set to invisible) each
with one squares worth of image in it. Then go to each layer and click
layer->new layer based slice. To finish up, goto save for web and
devices.. and save all slices, rename them “left” right” etc. and there
you go.
2) create a 3d environment in papervision3D with the folloing elements:
a) a cube with bitmap images mapped to the six inside faces
b) a free camer in the center of the cube
c) mouse events that rotated the camera around, giving the illusion of looking around the room
Let’s go:
1) first create a new actionscript project in Flex, name it Panorama.
2) make sure all the necessary class packages(org,com,caurina) are in your project folder. (they are included in the source);
3) we are going to create single class that extends Sprite and contains all of our code for the panorama:
What going on here is pretty simple, as far as PV3D projects go. We are
creating a cube, an array for the materialsList that we assign to the
cube, a URLRequest to load the bitmaps,a function to rotate the camera
around, a looped event that renders the scene.
(see actionscript code below - this is based on the Flash example here:)
package
{
import flash.display.*;
import flash.utils.Timer;
import flash.events.*;
import flash.net.*;
import flash.system.LoaderContext;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.cameras.FreeCamera3D;
import org.papervision3d.objects.Cube;
import org.papervision3d.materials.MaterialsList;
import org.papervision3d.materials.*
import org.papervision3d.core.proto.*
import caurina.transitions.*;
[SWF(frameRate=”60″)]
public class Panorama extends Sprite
{
private var container :Sprite;
private var scene :Scene3D;
private var camera :FreeCamera3D;
private var cube :Cube;
private var assetArray :Array;
private var bitMaps :Array;
private var count :Number;
private var panCube :Sprite;
private var currentCube;
private var front;
private var back;
private var bottom;
private var right;
private var left;
private var top;
public function Panorama()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
init3D();
loadAssets();
bitMaps = new Array();
stage.quality = StageQuality.LOW;
}
private function init3D():void
{
container = new Sprite();
addChild( container );
scene = new Scene3D( container );
camera = new FreeCamera3D();
camera.zoom = 1;
camera.focus = 800;
camera.z = 10;
}
private function loadAssets():void
{
count = 0;
assetArray = new Array(”back”, “front”, “right”, “left”, “top”, “down”);
loadOne();
}
private function loadOne():void
{
var loaD:Loader = new Loader();
loaD.contentLoaderInfo.addEventListener(Event.COMPLETE, progfin);
var urlreq:URLRequest = new URLRequest(assetArray[count]+”.jpg”);
loaD.load(urlreq);
panCube = new Sprite();
panCube.x = 30+(count*36);
panCube.y = 10;
panCube.alpha = 0;
currentCube = panCube;
addChild(panCube);
}
private function progfin(e:Event):void
{
var bm:Bitmap = e.target.content;
var bmm:BitmapData = bm.bitmapData;
bitMaps.push(bmm);
count+=1;
if(count < assetArray.length) {
loadOne();
} else {
createCube();
stage.addEventListener( Event.ENTER_FRAME, loop );
};
}
private function createCube()
{
var quality :Number = 24;
// Materials
var b = new BitmapMaterial( bitMaps[0] );
var f = new BitmapMaterial( bitMaps[1] );
var r = new BitmapMaterial( bitMaps[2] );
var l = new BitmapMaterial( bitMaps[3] );
var t = new BitmapMaterial( bitMaps[4] );
var d = new BitmapMaterial( bitMaps[5] );
b.smooth = true;
f.smooth = true;
r.smooth = true;
l.smooth = true;
t.smooth = true;
d.smooth = true;
b.oneSide = true;
f.oneSide = true;
r.oneSide = true;
l.oneSide = true;
t.oneSide = true;
d.oneSide = true;
var materials:MaterialsList = new MaterialsList(
{
front: f,
back: b,
right: r,
left: l,
top: t,
bottom: d
} );
var insideFaces :int = Cube.ALL;
var excludeFaces :int = Cube.NONE;
cube = new Cube( materials, 15000, 15000, 15000, quality, quality, quality, insideFaces, excludeFaces );
scene.addChild( cube, “Cube” );
}
private function renderNoMouse(e:Event):void
{
};
private function loop(event:Event):void
{
update3D();
}
private function update3D():void
{
// Pan
var pan:Number = camera.rotationY - 210 * container.mouseX/(stage.stageWidth/2);
pan = Math.max( -100, Math.min( pan, 100 ) ); // Max speed
camera.rotationY -= pan / 12;
// Tilt
var tilt:Number = 90 * container.mouseY/(stage.stageHeight/2);
camera.rotationX -= (camera.rotationX + tilt) / 12;
// Render
scene.renderCamera( this.camera );
}
private function go(e:Event)
{
scene.renderCamera( this.camera )
}
}
}