Author Topic: Camera UP vector  (Read 2227 times)

Offline Wojtek

  • int
  • **
  • Posts: 62
    • View Profile
Camera UP vector
« on: August 11, 2009, 10:47:20 pm »
Hello,

I would like to allow player to move camera around the player's ship to see it from different perspective.
The following image shows what I am planning to do: .

First I have tried to use Camera.lookAt() method, however when I run the game it does not work as I wanted.
It is difficult for me to explain why, so i attached 3 screenshots showing how it works with lookAt() method:



What I want is not to see the effect of rotating ship, but have the effect that player is 'flying' around the ship.


After that I have started playing with Camera.setOrientation() method. I am using following code to calculate direction vector:
Code: [Select]
SimpleVector direction = object.getTransformedCenter();
direction=direction.calcSub(camera.getPosition());

It seems to work for me, however I have no idea how to calcluate up vector. If I understand it correctly its purpose is to show the direction where is the 'sky' so it suppose to be a perpendicular vector to direction vector (I have showed it as a small blue line at the first picture). I am trying to make it work a second day but without result :( Can anyone help me and give a hint how to calculate it?

Thanks,
Wojtek

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Camera UP vector
« Reply #1 on: August 11, 2009, 11:50:00 pm »
You need at least one other vector, which you don't seem to have. Maybe another approach like in the topic "scene rotation" (code posted at the end)  helps? Sorry for not linking to it, but i have no idea how to copy and paste something on this phone i'm on...

Offline paulscode

  • double
  • *****
  • Posts: 863
    • View Profile
    • PaulsCode.Com
Re: Camera UP vector
« Reply #2 on: August 12, 2009, 02:38:28 am »
I have used setups kind of like this for a few of my projects.  What I usually do is create a "camera assembly" out of two or more dummy objects, then match the camera's position and orientation to one of them any time they move.  Something like this should work in your case:

Create the "camera assembly", and set up parent/child relationships:
Code: [Select]
Object3D ship = loadShip();  // get your ship somehow
Object3D pivot = Object3D.createDummyObj();
Object3D satellite = Object3D.createDummyObj();
ship.addChild( pivot );
pivot.addChild( satellite );

Move satellite to the camera "starting position":
Code: [Select]
satellite.translate(
    new SimpleVector( 0, -50, -50 ) );
// Look "down" toward the ship:
satellite.rotateAxis(
    satellite.getXAxis(),
    (float) Math.PI / 4.0f );
resetCameraPosition();

The "resetCameraPosition" method:
Code: [Select]
private void resetCameraPosition()
{
    SimpleVector look = new SimpleVector( satellite.getZAxis() ).normalize();
    SimpleVector up = new SimpleVector( satellite.getYAxis() ).normalize();
    camera.setOrientation( look, up );
    camera.setPosition( satellite.getTransformedCenter() );
}

Then, to zoom around the ship:
Code: [Select]
pivot.rotateAxis(
    pivot.getYAxis(),
    someAngle );
resetCameraPosition();

You'd also need to call "resetCameraPosition" any time you moved the ship, as well.

I haven't actually tested this exact setup (I just altered the code from one of my projects), but it should give you the general idea.

--EDIT--
One more note: if you wanted the ship to be able to move and rotate independantly from the camera assembly, just create a "master pivot" which both the ship and camera assembly are a child of:
Code: [Select]
Object3D ship = loadShip();  // get your ship somehow
Object3D pivot = Object3D.createDummyObj();
Object3D satellite = Object3D.createDummyObj();
Object3D masterPivot = Object3D.createDummyObj();
masterPivot.addChild( ship );
masterPivot.addChild( pivot );
pivot.addChild( satellite );
The rest of the above code would stay the same.  With this setup, any time you wanted to translate/rotate both the ship and camera, you'd manipulate the master pivot.  If you wanted to translate/rotate just the ship and not the camera, you'd manipulate the ship object.  If you wanted to translate/rotate just the camera and not the ship, you'd manipulate the pivot or satellite.
« Last Edit: August 12, 2009, 04:24:15 am by paulscode »

Offline paulscode

  • double
  • *****
  • Posts: 863
    • View Profile
    • PaulsCode.Com
Re: Camera UP vector
« Reply #3 on: August 12, 2009, 05:01:27 am »
Oops, I forgot that child rotation doesn't take parent's rotations into account (that's like the 10,000th time I made that same mistake).  Change the "resetCameraPosition" to this instead:

Code: [Select]
    private void resetCameraPosition()
    {
        camera.setBack( satellite.getWorldTransformation().invert3x3() );
        camera.setPosition( satellite.getTransformedCenter() );
    }

I just ran a quick test, and this seems to work fine.  Let me know if you have any problems.

Offline Wojtek

  • int
  • **
  • Posts: 62
    • View Profile
Re: Camera UP vector
« Reply #4 on: August 12, 2009, 07:43:12 pm »
Thank you both for quick help :)

First I have tried the solution presented by Egon (Scene Rotation topic: http://www.jpct.net/forum2/index.php/topic,977.0.html)
and it works even better than I assumed :)

There is a code that I used to for testing:
Code: [Select]
public class CameraPosition
{
    private float distance = 5;

    public void rotate(Camera cam, double x, double y)
    {
SimpleVector line = new SimpleVector(x, 0, y);
Matrix m = line.normalize().getRotationMatrix();
m.rotateAxis(m.getXAxis(), (float) -Math.PI / 2f);

SimpleVector shift = moveCameraToZeroPosition(cam);
cam.rotateAxis(m.invert3x3().getXAxis(), line.length() / 200f);
restoreCameraPosition(cam, shift);
    }

    private void restoreCameraPosition(Camera cam, SimpleVector shift)
    {
cam.moveCamera(Camera.CAMERA_MOVEOUT, distance);
shift.add(cam.getPosition());
cam.setPosition(shift);
    }

    private SimpleVector moveCameraToZeroPosition(Camera cam)
    {
cam.moveCamera(Camera.CAMERA_MOVEIN, distance);
SimpleVector shift = cam.getPosition();
cam.setPosition(new SimpleVector(0, 0, 0));
return shift;
    }

    public void zoom(Camera cam, double delta)
    {
distance -= delta;
cam.moveCamera(Camera.CAMERA_MOVEIN, (float) delta);
    }

    public void updateCamera(Object3D object, Camera cam)
    {
if (cam.getPosition().length() == 0)
    initCamera(object, cam);
moveCameraToZeroPosition(cam);
restoreCameraPosition(cam, object.getTransformedCenter());
    }

    private void initCamera(Object3D object, Camera cam)
    {
SimpleVector vec = new SimpleVector(0, 5, 5);
vec.add(object.getTransformedCenter());
cam.setPosition(vec);
cam.lookAt(object.getTransformedCenter());
    }
}

I have added two modifications to the code:
1. initCamera() if camera position is [0,0,0] - this helps me to set correct starting point for camera
2. Apart from moving camera using distance, moveCameraToZeroPosition() and restoreCameraPosition() methods resets the camera position to [0,0,0] (and returns the shift vector) and applies shift vector when camera position is restored. Having that, camera is able to follow moving objects.


I have not checked the second solution because the first one is ok for me and it also allows ship to rotate independently from camera.