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;
        }
    }
}

6 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
  5. Gan September 12, 2012 09:34

    This tutorial of yours is great… Thank you

Leave a comment

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