Papervision 3D camera orbit on all axis

A question that comes up often in the papervision3D community is how do you make a camera that will can orbit around a point on all axis, while maintaining a constant distance. Well with a little math we can do this. Here is a quick demonstration of a camera that does this. And just for fun, and because papervision is cool, the light is also rotating around the cubes in this example, so it will be changing also.

and the how:

// create some variables. Distance is how far away you want to be
var distance:Number = 950;
var theta:Number;
var phi:Number;

// on your loop
// update theta and phi based on your mouse positions, then update the camera position
theta = mouseX * Math.PI / 180;
phi = mouseY * Math.PI / 180;

camera.x = distance * Math.cos(theta) * Math.sin(phi)
camera.z = distance * Math.sin(theta) * Math.sin(phi)
camera.y = distance * Math.cos(phi);

and thats it!

Comments

Comment functionality has been disabled. Contact me on Twitter.

dougi said:

Hi, thanks for the sample but the scene sometimes disappears when you move up your mouse cursor.

Any idea ?

Tyler Egeto said:

Yes, when the phi is at perfect 0 (mouse.y == 0) it disappears, this has to do with the papervision3d camera engine. You can prevent it with this simple bit of code:

if(phi == 0){ phi += .01; }

Then phi will never be a zero. Problem solved!

jc said:

This is very helpful - been searching for a simple answer to this. However I notice that when my mouse hits a certain point vertically (near the bottom of your test movie), the green and blue boxes flip positions. The motion flips as well, instead of the boxes rotating opposite the mouse movement, they start rotating the same direction. Any ideas on how to compensate for that?

Tyler Egeto said:

New code just posted to solve this problem! see my home page.

luke said:

hey tyler, this works well in your example but in my movie the camera moves to the left and right and distorts noticeably. i’m not sure if it’s because i’m using larger units, or more objects, or what. here’s my code, which should be pretty easy to follow (hopefully).

package {
    import PaperBase;
    import org.papervision3d.objects.primitives.Cone;
    import org.papervision3d.materials.BitmapFileMaterial;
    import org.papervision3d.lights.PointLight3D;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.materials.special.MovieAssetParticleMaterial;
    import org.papervision3d.core.geom.Particles;
    import org.papervision3d.core.geom.renderables.Particle;

    public class cloud extends PaperBase {
        public var distance:Number=3000;
        public var theta:Number;
        public var phi:Number;
        public function cloud() {
            init();
        }
        override protected function init3d():void {
            for (var i=0; i<20; i++) {
                var randcol:uint=Math.round(Math.random()*0xFFFFFF);
                var mat:FlatShadeMaterial = new FlatShadeMaterial(new PointLight3D(), 0xFFFF00, randcol);
                var materialsList:MaterialsList = new MaterialsList();
                materialsList.addMaterial(mat,"all");
                var cube:Cube = new Cube(materialsList,250,250,250);
                cube.x = Math.random()*2000-1000;
                cube.y = Math.random()*2000-1000;
                cube.z = Math.random()*2000-1000;
                cube.rotationX = Math.random()*360;
                cube.rotationY = Math.random()*360;
                cube.rotationZ = Math.random()*360;
                default_scene.addChild(cube);
                //
                var spm:MovieAssetParticleMaterial = new MovieAssetParticleMaterial("Circle",true);
                spm.interactive = true;
                spm.smooth = true;
                var particles3D:Particles = new Particles("ParticleContainer#"+i);
                var p:Particle = new Particle(spm,1,Math.random()*2000-1000,Math.random()*2000-1000,Math.random()*2000-1000);
                particles3D.addParticle(p);
                default_scene.addChild(particles3D);
            }

        }
        override protected function processFrame():void {
            //update theta and phi based on your mouse positions, then update the default_camera position
            theta = mouseX*Math.PI/180;
            phi = mouseY*Math.PI/180;
            default_camera.x = distance * Math.cos(theta) * Math.sin(phi);
            default_camera.z = distance * Math.sin(theta) * Math.sin(phi);
            default_camera.y = distance * Math.cos(phi);
            /*
            // thanks to Peter Kapelyan for this little gem - http://www.flashnine.com/?p=23
            // return the default_camera to zero, then rotate it by 1 on the Y axis, then move back away
            default_camera.x=default_camera.y=default_camera.z=0;
            default_camera.yaw(1);
            default_camera.moveBackward(3000);
            */
        }
    }
}

Gunther said:

There is nothing wrong with the code. What you experience is the effect of your camera passing the top/zenit of the sphere (you’re turning over the top of your object). The camera is than in upside down position (which is ok since that is what you want) BUT the lookat function makes the camera flip in normal position again … creating the effect that your scene flips.

Solution, prevent your lookat function to do the camera flip.

if (Math.sin(phi) < 0) upVector = new Number3D(0, -1, 0); else upVector = new Number3D(0, 1, 0);

camera.lookAt(your_object, upVector);

this piece of code checks wether you pass the zenit and changes the up direction accordingly. If you use this code you will see that your camera/scene no longer flips and you can orbit smoothly around your object in the vertical direction.

Tyler Egeto said:

@Gunther

Awesome! I had no idea that second parameter was there. Thanks so much!

@luke

I’m not positive, but I think it being caused by the fact that your cube is offset from the center. So rather than rotating around the center of the scene, you will want to rotate around the center of the cube.

Jose said:

It is better in this way (I think)

var radio:Number =250;
var gradosX:Number = -(mouseX*360)/stage.width;
var gradosY:Number = (mouseY*360)/stage.height;
var theta:Number = gradosX*Math.PI/180;
var phi:Number = gradosY*Math.PI/180;
phi = Math.max(0.1,phi);
camera.x = radio * Math.cos(theta) * Math.sin(phi)
camera.z = radio * Math.sin(theta) * Math.sin(phi)
camera.y = radio * Math.cos(phi);

dan said:

Great piece of code. Big help. You’re a genius.

Casey said:

@Gunther - I’ve implemented your code in this example and it doesn’t work. Tracing the upVector value indicates that it is properly switching when I approach the zenith, but the camera still flips. What could I be doing wrong?

Is there a working example of this technique somewhere?

Tyler Egeto said:

Hi Casey,

It should work, I don’t have code off hand, but I’ve implemented it without issue like that. Feel free to drop me an email if you want me to take a look.

Jordi said:

Hi Gunther!

I have the same problem with flipping, but I’ve used your solution and it still doesn’t runs well. Where must I use this code?

I have this on my enter frame handler:

===================

gradosX = -(mouseX*360)/stage.stageWidth; gradosY = (mouseY*360)/stage.stageHeight; theta = gradosX*Math.PI/180; phi = gradosY*Math.PI/180;

phi = Math.max(0.1,phi);

if (Math.sin(phi) < 0) upVector = new Number3D(0, -1, 0); else upVector = new Number3D(0, 1, 0);

camera.lookAt(collada, upVector); camera.x = radio * Math.cos(theta) * Math.sin(phi) camera.z = radio * Math.sin(theta) * Math.sin(phi) camera.y = radio * Math.cos(phi);

// Render scene singleRender();

===================

Thanks for your help!

Recent & Popular Articles

Browse All >