daflash.com | Flash Development

CAT | Code

A skybox is a cube that is textured with a distant landscape on its 6 sides which is viewed by a camera at its center. This creates the illusion that you are inside a vast area without having to render too many polygons.

Our first step will be creating the textures. I will be using Terragen Classic v0.9 to render the landscape, you can find it here. Once you’ve downloaded, installed, and started up Terragen you’ll be presented with a window like this:

Terragen-1

In the ‘Rendering Control’ window match the settings to the image below, starting from the red square at the top.

Terragen-Rendering-Control

Now click on the ‘Image Size…’ and then the ‘Camera Settings…’ buttons and match them to the following images. If you want more detailed images you could set the width and height in ‘Render Settings’ to 1024.

Terragen-Render-Settings

Terragen-Camera-Settings

Now open the ‘Cloudscape’ window by clicking the icon to the left with the clouds on it.

Terragen-Cloudscape-Button

Change the ‘Sky size’ value to 8192 and x out.

Terragen-Cloudscape

Lets see what it looks like! Click the ‘Render Preview’ button in the ‘Rendering Control’ window, you should see something like this:

Terragen-Render-Preview

So far so good, lets add some terrain and water. In the ‘Landscape’ window press the ‘Generate Terrain…’ button.

Terragen-Landscape

Now press the ‘Generate Terrain’ button in the new window and close it.

Terragen-Genesis

Back in the ‘Landscape’ window press the ‘Modify…’ button.

Terragen-Landscape-Modify

In the new window set the height range from -16 to 16, click the ‘Set Height Range’ button, and close the window.

Terragen-Terrain-Modification

Lets preview our image again, you should get something like this:

Terragen-Render-Preview-2

Now we are ready to render our 6 textures for the skybox. For the front texture we will set ‘Camera Orientation’ to 0, 0, 0 (head, pitch, bank) and press the ‘Render Image’ button.

Terragen-Rendering-Control-2

Once it has finished rendering click the ‘Save’ button and name it ‘front.bmp’.

Terragen-Rendering

Repeat the last few steps for the top, bottom, left, right, and back textures using the camera orientation values from the image below.

Terragen-Skybox

You should now have 6 bmp files, you will need to convert them to JPEGs in order for them to work with Papervision. Lets take a look at the ActionScript, I’m using Flex Builder 3 with Papervision 2. Here is the SkyboxComponent class:

package {
    import flash.events.Event;
   
    import mx.core.UIComponent;
    import mx.events.ResizeEvent;
   
    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.Viewport3D;

    public class SkyboxComponent extends UIComponent{
        public var boxWidth:int=512;
        public var boxHeight:int=512;
       
        public var front:String="images/front.jpg";
        public var back:String="images/back.jpg";
        public var left:String="images/left.jpg";
        public var right:String="images/right.jpg";
        public var top:String="images/top.jpg";
        public var bottom:String="images/bottom.jpg";
       
        private var viewport:Viewport3D;
        private var scene:Scene3D;
        private var camera:Camera3D;
        private var renderer:BasicRenderEngine;
       
        public function SkyboxComponent(){
            super();
            //init on 'added_to_stage' event so width and height are set properly
            this.addEventListener(Event.ADDED_TO_STAGE,init);
            //resize viewport when component resizes
            this.addEventListener(ResizeEvent.RESIZE,onResize);
        }
       
        private function init(event:Event):void{
            //setup papervision
            viewport=new Viewport3D(this.width,this.height);
            renderer=new BasicRenderEngine();
            scene=new Scene3D();
            camera=new Camera3D(60,10,Number.POSITIVE_INFINITY,true);
            camera.zoom=10;
            camera.focus=Math.round(((boxWidth+boxHeight)/2)/20);
            camera.x=0;
            camera.y=0;
            camera.z=0;
            this.addChild(viewport);
            //here we will place the images, by using planes instead of a cube we can ensure correct image orientation
            //front plane
            var mat:BitmapFileMaterial=new BitmapFileMaterial(front);
            var plane:Plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.z=boxWidth/2;
            scene.addChild(plane);
            //back plane
            mat=new BitmapFileMaterial(back);
            plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.z=-(boxWidth/2);
            plane.rotationY=180;
            scene.addChild(plane);
            //left plane
            mat=new BitmapFileMaterial(left);
            plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.x=-(boxWidth/2);
            plane.rotationY=-90;
            scene.addChild(plane);
            //right plane
            mat=new BitmapFileMaterial(right);
            plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.x=boxWidth/2;
            plane.rotationY=90;
            scene.addChild(plane);
            //top plane
            mat=new BitmapFileMaterial(top);
            plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.y=boxHeight/2;
            plane.rotationX=-90;
            scene.addChild(plane);
            //bottom plane
            mat=new BitmapFileMaterial(bottom);
            plane=new Plane(mat,boxWidth,boxHeight,8,8);
            plane.y=-(boxHeight/2);
            plane.rotationX=90;
            scene.addChild(plane);
            //render on 'enter_frame' event
            this.addEventListener(Event.ENTER_FRAME,onEnterFrame);
        }
       
        //the camera will always stay in the center, only its rotation should be changed
        public function setCameraRotation(x:Number,y:Number,z:Number):void{
            camera.rotationX=x;
            camera.rotationY=y;
            camera.rotationZ=z;
        }
       
        private function onEnterFrame(event:Event):void{
            //render
            renderer.renderScene(scene,camera,viewport);
        }
       
        private function onResize(event:ResizeEvent):void{
            if(viewport!=null){
                //resize viewport
                viewport.viewportWidth=this.width;
                viewport.viewportHeight=this.height
            }
        }
    }
}

Here is the class that rotates a camera around some cubes. If you were making a game or application this would contain the 3d objects you are displaying. The reason I separated this from the SkyboxComponent class is because you may want to clip distant polygons by setting the cameras far variable for better performance, by keeping them separated the skybox will never get clipped out.

package {
    import flash.events.Event;
   
    import mx.core.UIComponent;
    import mx.events.ResizeEvent;
   
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.materials.special.CompositeMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.Viewport3D;

    public class RotateAroundCubeComponent extends UIComponent{
        private var viewport:Viewport3D;
        private var scene:Scene3D;
        private var camera:Camera3D;
        private var renderer:BasicRenderEngine;
       
        private var rotationX:Number=0;
        private var rotationY:Number=0;
        private var rotationZ:Number=0;
       
        private var randomizeRotationFrame:int=0;
       
        public function RotateAroundCubeComponent(){
            super();
            //init on 'added_to_stage' event so width and height are set properly
            this.addEventListener(Event.ADDED_TO_STAGE,init);
            //resize viewport when component resizes
            this.addEventListener(ResizeEvent.RESIZE,onResize);
        }
       
        private function init(event:Event):void{
            //setup papervision
            viewport=new Viewport3D(this.width,this.height);
            renderer=new BasicRenderEngine();
            scene=new Scene3D();
            camera=new Camera3D(60,10,2000,true);
            camera.zoom=100;
            camera.focus=10;
            this.addChild(viewport);
            //create cubes material
            var cubeMaterial:CompositeMaterial=new CompositeMaterial();
            var wireframeMaterial:WireframeMaterial=new WireframeMaterial(0x000000,1,1);
            var colorMaterial:ColorMaterial=new ColorMaterial(0xFFFFFF);
            cubeMaterial.addMaterial(wireframeMaterial);
            cubeMaterial.addMaterial(colorMaterial);
            var materialList:MaterialsList=new MaterialsList();
            materialList.addMaterial(cubeMaterial,"all");
            //create cubes
            var cube:Cube=new Cube(materialList,20,20,20);
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.x=-40;
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.x=40;
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.z=40;
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.z=-40;
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.y=40;
            scene.addChild(cube);
            cube=new Cube(materialList,20,20,20);
            cube.y=-40;
            scene.addChild(cube);
            //render on 'enter_frame' event
            this.addEventListener(Event.ENTER_FRAME,onEnterFrame);
        }
       
        private function onEnterFrame(event:Event):void{
            //randomize rotation value
            if(randomizeRotationFrame%200==0){
                rotationX=(Math.random()/2)-.25;
                rotationY=(Math.random())-.5;
                rotationZ=(Math.random()/8)-.0625;
            }
            randomizeRotationFrame++;
            //place camera
            camera.x=0;
            camera.y=0;
            camera.z=0;
            camera.rotationX+=rotationX;
            camera.rotationY+=rotationY;
            camera.rotationZ+=rotationZ;
            camera.moveBackward(1600);
            //render
            renderer.renderScene(scene,camera,viewport);
        }
       
        public function getCameraRotation():Object{
            if(camera!=null){
                return {x:camera.rotationX,y:camera.rotationY,z:camera.rotationZ};
            }
            return null;
        }
       
        private function onResize(event:ResizeEvent):void{
            if(viewport!=null){
                //resize viewport
                viewport.viewportWidth=this.width;
                viewport.viewportHeight=this.height
            }
        }
    }
}

And here is the applications MXML file. Notice that I’m updating the skybox cameras rotation based on the rotatingCube camera with an enterFrame event.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
    <mx:Script>
        <![CDATA[
            //update the skybox camera based on the rotatingCube camera
            private function setSkyboxCameraRotation(event:Event):void{
                var cameraRotation:Object=rotatingCube.getCameraRotation();
                if(cameraRotation!=null){
                    skybox.setCameraRotation(cameraRotation.x,cameraRotation.y,cameraRotation.z);
                }
            }
        ]]>
    </mx:Script>
    <mx:HBox width="100%" height="100%" verticalAlign="middle" horizontalAlign="center">
        <mx:Panel title="Skybox Example" paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10" width="90%" height="90%">
            <mx:Canvas width="100%" height="100%">
                <local:SkyboxComponent id="skybox" boxWidth="512" boxHeight="512" front="images/front.jpg" back="images/back.jpg" left="images/left.jpg" right="images/right.jpg" top="images/top.jpg" bottom="images/bottom.jpg" width="100%" height="100%"/>
                <local:RotateAroundCubeComponent id="rotatingCube" enterFrame="setSkyboxCameraRotation(event);" width="100%" height="100%"/>
            </mx:Canvas>
        </mx:Panel>
    </mx:HBox>
</mx:Application>

You can download the Flex project here.

actionscript, flex, Papervision, terragen Hide

Oct/09

10

Using Papervision as a Component in Flex

It may come in handy at times to take advantage of all the UI components in Flex to manipulate a 3d scene in Papervision. In this example I add a Papervision component that renders a sphere to the applications MXML document. I’m using Papervision 2 and Flex Builder 3.

Here is the component class:

package {
    import flash.events.Event;
   
    import mx.core.UIComponent;
    import mx.events.ResizeEvent;
   
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.materials.special.CompositeMaterial;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.Viewport3D;

    public class PV3DComponent extends UIComponent{
        private var viewport:Viewport3D;
        private var scene:Scene3D;
        private var camera:Camera3D;
        private var renderer:BasicRenderEngine;
        private var sphere:Sphere;
        private var rotationX:Number=.5;
        private var rotationY:Number=-.5;
        private var rotationZ:Number=.5;
       
        public function PV3DComponent(){
            super();
            //init on 'added_to_stage' event so width and height are set properly
            this.addEventListener(Event.ADDED_TO_STAGE,init);
            //resize viewport when component resizes
            this.addEventListener(ResizeEvent.RESIZE,onResize);
        }
       
        private function init(event:Event):void{
            //setup papervision
            viewport=new Viewport3D(this.width,this.height);
            renderer=new BasicRenderEngine();
            scene=new Scene3D();
            camera=new Camera3D(90,10,2000,true);
            camera.z=-100
            camera.zoom=10;
            camera.focus=100;
            this.addChild(viewport);
            //create sphere
            var sphereMaterial:CompositeMaterial=new CompositeMaterial();
            var wireframeMaterial:WireframeMaterial=new WireframeMaterial(0x000000,1,1);
            var colorMaterial:ColorMaterial=new ColorMaterial(0xFFFFFF);
            sphereMaterial.addMaterial(wireframeMaterial);
            sphereMaterial.addMaterial(colorMaterial);
            sphere=new Sphere(sphereMaterial,10,16,16);
            scene.addChild(sphere);
            //render on 'enter_frame' event
            this.addEventListener(Event.ENTER_FRAME,onEnterFrame);
        }
       
        private function onEnterFrame(event:Event):void{
            //rotate sphere
            sphere.rotationX+=rotationX;
            sphere.rotationY+=rotationY;
            sphere.rotationZ+=rotationZ;
            //render
            renderer.renderScene(scene,camera,viewport);
        }
       
        private function onResize(event:ResizeEvent):void{
            if(viewport!=null){
                //resize viewport
                viewport.viewportWidth=this.width;
                viewport.viewportHeight=this.height
            }
        }
       
        public function setSphereRotation(x:Number,y:Number,z:Number):void{
            rotationX=x;
            rotationY=y;
            rotationZ=z;
        }
    }
}

And here is the applications MXML:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:classes="classes.*" xmlns:local="*">
    <mx:Script>
        <![CDATA[
            import mx.events.SliderEvent;
            private function onRotationSliderChange(event:SliderEvent):void{
                pv3dComponent.setSphereRotation(xRotationSlider.value,yRotationSlider.value,zRotationSlider.value);
            }
        ]]>
    </mx:Script>
    <mx:HBox width="100%" height="100%" verticalAlign="middle" horizontalAlign="center">
        <mx:Panel title="Papervision3D Flex Component Example" paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10" width="90%" height="90%">
            <mx:VBox width="100%" height="100%">
                <local:PV3DComponent id="pv3dComponent" width="100%" height="100%"/>
                <mx:HRule width="100%"/>
                <mx:HBox width="100%">
                    <mx:Label text="X Rotation:" fontWeight="bold"/>
                    <mx:HSlider id="xRotationSlider" minimum="-5" maximum="5" value=".5" change="onRotationSliderChange(event);" width="100%"/>
                </mx:HBox>
                <mx:HBox width="100%">
                    <mx:Label text="Y Rotation:" fontWeight="bold"/>
                    <mx:HSlider id="yRotationSlider" minimum="-5" maximum="5" value="-.5" change="onRotationSliderChange(event);" width="100%"/>
                </mx:HBox>
                <mx:HBox width="100%">
                    <mx:Label text="Z Rotation:" fontWeight="bold"/>
                    <mx:HSlider id="zRotationSlider" minimum="-5" maximum="5" value=".5" change="onRotationSliderChange(event);" width="100%"/>
                </mx:HBox>
            </mx:VBox>
        </mx:Panel>
    </mx:HBox>
</mx:Application>

You can download the Flex project here.

actionscript, flex, Papervision Hide

Find it!

Theme Design by devolux.org