Setting a Camera's orientation

Started by paulscode, October 13, 2008, 01:03:46 AM

Previous topic - Next topic

paulscode

What is the best way to set a camera's orientation?  I can use the lookAt() method to tell the camera which direction to point, but I am having a little trouble figuring out a simple way to set the camera's up direction.

EgonOlsen

There is none of these famous direction-and-up-vector-methods to set the camera orientation in jPCT. Mainly because i never saw the point of this nor did i ever needed something like this. It shouldn't be too hard to find the basic math for this on the net. I can add it, but i'm a little short on time ATM, so i think you'll faster by doing it on your own. Maybe you can post the code, so that i can add it faster... ;D
Maybe we already have some code for this in the forum...i dimly remember, but i can't find it because the search function simply doesn't work for me.
Personally, i'm always working with rotations and lookAt. lookAt doesn't screw up the up vector for me, but it may in some situations, where the source and target orientation differ significantly.

paulscode

No problem.  I wasn't really able to find a useful formula online to use rotations to accomplish the same effect.  I did, however, come up with my own formula.  I am no math wiz, so the main difficulty I was having was the fact that the camera's rotate methods are all relative to its current orientation (compounded by my lack of experience with 3D geometry).  I believe I have finally solved the problem, though.

Here are the two methods for setting camera look-at and up directions using normalized vectors.  Note: I've only run some very basic tests on the setUpDirection() method, but it seems to work.  I'll write another post here after I've had a chance to use the method more extensively to see if there is an error in my formula.

/**
* Sets the specified Camera's look-at direction. 
* @param c Camera to use. 
* @param v Normalized vector indicating the look-at direction. 
*/
    public void setLookDirection( Camera c, SimpleVector v )
    {
        SimpleVector l = new SimpleVector( v );
        l.add( c.getPosition() );
        c.lookAt( l );
    }
   
/**
* Sets the specified Camera's up direction. 
* @param c Camera to use. 
* @param v Normalized vector indicating the up direction. 
*/
    public void setUpDirection( Camera c, SimpleVector v )
    {
        SimpleVector previousUp = new SimpleVector( c.getUpVector() );
       
        // TODO:  Remove this hack after the next release of jPCT!
        previousUp.y = -previousUp.y;
       
        float distance = previousUp.calcSub( v ).length();
        float angle = (float) Math.acos( ( 2.0d - (distance * distance) )
                                         / 2.0d );
        if( previousUp.x == -v.x && previousUp.y == -v.y &&
            previousUp.z == -v.z )
            c.rotateAxis( c.getDirection(), angle );
        else
            c.rotateAxis( previousUp.calcCross( v ).normalize(), angle );
    }


In case you are wondering about that if statement, I put it there to handle the case where the new up-direction is exactly opposite to the previous up-direction (for example, if previous up is -y and new up is y).  In such a case, a cross-vector can not be calculated to indicate which axis to rotate around.  Also, you may be wondering why I didn't always use c.getDirection() as the axis to rotate around.  That is because I am using the law of cosines, which was designed to calculate the angle of a triangle given its sides, so it is limited to angles between 0 and pi.  In order to get a full 2 pi of rotation, I calculate the cross-vector, which reverses when the angle becomes greater than pi.

paulscode

I simplified the setUpDirection() method somewhat using a dot product (at the suggestion of a friend on the gamedev forums):

    public void setUpDirection( Camera c, SimpleVector v )
    {
        SimpleVector previousUp = new SimpleVector( c.getUpVector() );
       
        // TODO:  Remove this hack after the next release of jPCT!
        previousUp.y = -previousUp.y;
       
        float angle = (float) Math.acos( previousUp.calcDot( v ) );

        if( previousUp.x == -v.x && previousUp.y == -v.y &&
            previousUp.z == -v.z )
            c.rotateAxis( c.getDirection(), angle );
        else
            c.rotateAxis( previousUp.calcCross( v ).normalize(), angle );
    }

paulscode

#4
Ok, I've started using setUpDirection() in my program, and there are problems.  It either means that there is a problem with the setUpDirection() formula or it means that I am using incorrect up and look-at vectors.  I'd like to rule out the latter.

I have two Object3D's: a pivot and a satellite.  The satellite is child to the pivot.  I am trying to get the camera to orbit around the pivot by matching the position and orientation of the satellite.  I am using the following method for that:

    private void resetCameraPosition()
    {
        SimpleVector look = new SimpleVector( pivot.getZAxis() );
        SimpleVector up = new SimpleVector( pivot.getYAxis() );
        up.y = -up.y;
       
        camera.setPosition( satellite.getTransformedCenter() );
        setLookDirection( camera, look.normalize() );
        setUpDirection( camera, up.normalize() );
    }


This is not behaving the way I would expect it to.  Calling pivot.rotateAxis( pivot.getYAxis(), angle ) seems to work perfectly.  The problem seems to arise when when I call pivot.rotateAxis( pivot.getXAxis(), theta ).  Rotating the object around its x-axis followed by rotations around the y-axis result in the camera orientation being incorrect.

So to rule out the resetCameraPosition() method as the cause of this problem, is there a problem with using the Z and Y axis of the pivot as look-at and up directions?


-- EDIT --
I uploaded a demo applet which demonstrates the camera orbiting problem.  Arrow keys are used to orbit the camera around the tree (note: the tree's position and orientation are not changing, just the camera).
http://www.paulscode.com/source/CameraOrbit

I have the applet outputting the up-vector to the Java console.

EgonOlsen

Quote from: paulscode on October 14, 2008, 02:08:31 AM
I have two Object3D's: a pivot and a satellite.  The satellite is child to the pivot.  I am trying to get the camera to orbit around the pivot by matching the position and orientation of the satellite.  I am using the following method for that:
Maybe you don't need all this stuff then and just feeding the satellite's rotation matrix (or the inverse...) into the camera will do?

Melssj5

Hi, I dont really know if this helps but when I was doing the car demo I moved the camera around the car, firstly I tried to do it calculating the position and direction it should have for an angle when orbiting but I never could do it well and I was good at geometry and trigonometry. I found a much better way to do it and I guess it could be usefull for you. for each movement do:

1.- Save the distance between teh camera and the orbitong center
2.- Put the camera on the ponit you are watching (orbiting center)
3.- rotate the camera and move the camera out the original distance
4.- render

On that way the camera wont act like that. Hope I correctly understood what are you trying to do.
Nada por ahora

paulscode

Both of those ideas would accomplish what I want to do, so I may end up using one of them.  I would like to figure out a good way of look-at / up-direction orientation (just because I got used to that methodology while working with OpenAL), but I can always learn a new way ;D

Quote from: EgonOlsen on October 14, 2008, 07:33:44 PM
Maybe you don't need all this stuff then and just feeding the satellite's rotation matrix (or the inverse...) into the camera will do?
BTW, how does one feed the rotation matrix into the camera?  I assume with methods like getRotationMatrix() and setRotationMatrix().  (I'll read the JavaDocs when i get back home)

Melssj5

o the camera are setBack and getBack methods! Those are the ones form the camera.
Nada por ahora

paulscode

Quote from: Melssj5 on October 14, 2008, 09:42:11 PM
o the camera are setBack and getBack methods! Those are the ones form the camera.

Hmm, so I would do something like this then?

myCamera.setBack( myObject3D.getRotationMatrix() );


I'll try this and see if it works.

Melssj5

Nada por ahora

EgonOlsen

You better clone the matrix before setting it. Otherwise, it's the same instance in Camera as it is in the Object3D, which may lead to all kind of strange effects. If that doesn't work, try an invert3x3() on it before feeding it to the camera. I'm not sure which way is correct, because when thinking about the camera, you have to "invert" some things, because a rotation of the camera actually is a rotation of the world in opposite direction...but i'm far too lazy ATM to think about this in detail, so just try it out... ;D

paulscode

Thanks that worked beautifully.  The working method:


    private void resetCameraPosition()
    {
        camera.setPosition( satellite.getTransformedCenter() );

        Matrix m = pivot.getRotationMatrix().cloneMatrix().invert3x3();
        camera.setBack( m );
    }


Working camera-orbit demo applet:
http://www.paulscode.com/source/CameraOrbit/MatrixMethod/

This will serve my purpose for now, so unless I get lucky and someone posts a working formula on one of my threads that I started on a couple of other programming forums, I will not pursue the look-at / up-direction route.

paulscode

I've got a working formula for setting the camera orientation based on look-direction and up-direction vectors  :D

I went a different route here and converted the direction vectors into a rotation matrix (much simpler than trying to figure out the trigonometry).  I haven't seen any problems at all in my tests:

    public void setOrientation( Camera camera, SimpleVector look, SimpleVector up )
    {
        SimpleVector right = up.calcCross( look ).normalize();
        Matrix m = new Matrix();
       
        m.set( 0, 0, right.x );
        m.set( 1, 0, right.y );
        m.set( 2, 0, right.z );
        m.set( 3, 0, 0.0f );
       
        m.set( 0, 1, up.x );
        m.set( 1, 1, up.y );
        m.set( 2, 1, up.z );
        m.set( 3, 1, 0.0f );
       
        m.set( 0, 2, look.x );
        m.set( 1, 2, look.y );
        m.set( 2, 2, look.z );
        m.set( 3, 2, 0.0f );
       
        m.set( 0, 3, 0.0f );
        m.set( 1, 3, 0.0f );
        m.set( 2, 3, 0.0f );
        m.set( 3, 3, 1.0f );
       
        camera.setBack( m );
    }


Here is the Camera Orbiter demo again, this time using the new method:
http://www.paulscode.com/source/CameraOrbit/Vectors2Matrix/

EgonOlsen

Looking at it, it is so logical that it hurts...i'll add it to the next release.