Smooth Orbiting Camera in Papervision 3D

While working with papervision, I realized papervision is missing something like hovering camera (read section Orbiting your scene using the Mouse). I decided to simulate it by using Camera3D.orbit() method. And here is the result:

package
{
    import __AS3__.vec.Vector;

    import caurina.transitions.Tweener;

    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.materials.ColorMaterial;
    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;

    [SWF(width="465", height="465", frameRate="30", backgroundColor="#000000")]

    public class Orbiting extends Sprite
    {
        private static const SMOOTHNESS:uint = 10; // more means smoother
        private static const SENSITIVITY:Number = 5; // rotation speed
        public static const W:uint = 465;
        public static const H:uint = 465;

        private var camera:Camera3D = new Camera3D();
        private var viewport:Viewport3D = new Viewport3D(W, H);
        private var scene:Scene3D = new Scene3D();
        private var renderer:BasicRenderEngine = new BasicRenderEngine();
        private var cube1:Cube;
        private var cube2:Cube;
        private var cube3:Cube;

        public var cameraPitch:Number = 60;
        public var cameraYaw:Number = -50;
        private var isCameraRotating:Boolean;
        private var previousMousePoint:Point;
        private var dxList:Vector.<Number> = new Vector.<Number>();
        private var dyList:Vector.<Number> = new Vector.<Number>();

        public function Orbiting()
        {
            super();

            var materials:MaterialsList = new MaterialsList();
            materials.addMaterial(new ColorMaterial(0xff0000), "front");
            materials.addMaterial(new ColorMaterial(0x00ff00), "back");
            materials.addMaterial(new ColorMaterial(0x0000ff), "left");
            materials.addMaterial(new ColorMaterial(0x00ffff), "right");
            materials.addMaterial(new ColorMaterial(0xffff00), "top");
            materials.addMaterial(new ColorMaterial(0xff00ff), "bottom");

            cube1 = new Cube(materials);
            scene.addChild(cube1);

            cube2 = new Cube(materials);
            cube2.x = 700;
            scene.addChild(cube2);

            cube3 = new Cube(materials);
            cube3.x = -700;
            scene.addChild(cube3);

            addChild(viewport);

            camera.zoom = 40;
            camera.x = 1000;
            updateCamera();

            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
            stage.addEventListener(Event.ENTER_FRAME, enterFrame);
        }

        private function mouseUp(event:Event):void
        {
            isCameraRotating = false;
        }

        private function mouseDown(event:Event):void
        {
            isCameraRotating = true;
            previousMousePoint = new Point(mouseX, mouseY);
            dxList = new Vector.<Number>();
            dyList = new Vector.<Number>();
        }

        private function enterFrame(event:Event):void
        {
            renderer.renderScene(scene, camera, viewport);

            if(!isCameraRotating)
                return;
            if(previousMousePoint.x == mouseX
                && previousMousePoint.y == mouseY)
            {
                dxList.shift();
                dyList.shift();
                return;
            }

            var dx:Number = previousMousePoint.x - mouseX;
            var dy:Number = previousMousePoint.y - mouseY;
            Tweener.addTween(this, {time: 2, onUpdate:updateCamera,
                cameraYaw:cameraYaw + calc(dxList, dx),
                cameraPitch:cameraPitch + calc(dyList, dy),
                transition: "easeOutCubic"});

            previousMousePoint = new Point(mouseX, mouseY);
        }

        private function updateCamera():void
        {
            cameraYaw %= 360;

            cameraPitch %= 360;
            cameraPitch = cameraPitch > 0 ? cameraPitch : 0.0001;
            cameraPitch = cameraPitch < 180 ? cameraPitch : 179.9999;

            camera.orbit(cameraPitch, cameraYaw, true, cube1);
        }

        private function calc(list:Vector.<Number>, d:Number):Number
        {
            if(list.length > SMOOTHNESS)
                list.shift();
            list.push(d);

            var result:Number = 0;
            for each(var item:Number in list)
                result += item;
            return result / list.length * SENSITIVITY;
        }
    }
}

5 comments so far

  1. martin June 11, 2010 13:35

    This is awesome, just what i neede, i love u !!!!

  2. Kevin Burke October 13, 2010 21:36

    I LOVE YOU MORE! OH MY GOD! PERFECT!!!!!!!!!!!!!!

  3. Adam May 24, 2011 18:34

    Hi Jozef,

    Great post, works a treat – could you give a hint on how I would add to this to make the camera rotate around a sphere to look at a given point on that sphere (for example a marker on the sphere which when clicked, makes the camera tween around the sphere to look at it)? The camera should maintain a constant distance from the sphere.

    Many thanks
    Adam

  4. Jozef Chúťka May 25, 2011 09:56

Leave a comment

Please be polite and on topic. Your e-mail will never be published.

Get Adobe Flash playerPlugin by wpburn.com wordpress themes